Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pure pattern-matching #152

Merged
merged 6 commits into from
Nov 19, 2024
Merged

Pure pattern-matching #152

merged 6 commits into from
Nov 19, 2024

Conversation

ppolesiuk
Copy link
Member

@ppolesiuk ppolesiuk commented Oct 27, 2024

This pull-request aims to resolve #149. This will be done in the following steps

  • Add flag about strictly positive recursion to definitions of ADT in Unif
  • Add flag about strictly positive recursion in ADT environment (TypeInference.Module.adt_info)
  • Recognition of strictly positive recursive types in TypeInference
  • Use information about strictly positive recursion in the effect of pattern-matching (TypeInference.Pattern)
  • Update translation to Core
  • Add flag about strictly positive recursion to definitions of ADT in Core
  • Add effect of pattern-matching to TData type in Core
  • Adjust internal type-checker for Core

Added effect to type shape proofs in Core. This effect describes the
effect of pattern-matching. In this commit, it is always NTerm effect,
but we are going to recognize positively-recursive types, and attach
pure effect to shape proofs of such ADTs.
This commit introduces `strictly_positive` flag to ADT definitions in
Core. ADTs marked as strictly positively recursive have the witness with
pure effect attached, so they can be deconstructed without introducing
NTerm effect.
@ppolesiuk ppolesiuk requested a review from forell October 27, 2024 16:49
@ppolesiuk ppolesiuk marked this pull request as ready for review November 3, 2024 09:25
@ppolesiuk ppolesiuk requested a review from Foxinio November 5, 2024 18:54
@ppolesiuk
Copy link
Member Author

This implementation has some slight problem with generated accessors for recursive records. For instance, when we write the following code

data rec  T = { x : T }
let f (t : T) = t.x

the f function is impure. This problem comes from the desugaring of recursive records. The above definition is a syntactic sugar for

rec
  data T = T of { x : T }
  method x { self = T { x } : T } = x
end

Notice, that the method is in recursive block, and therefore is impure, even if the pattern-matching on self is pure.

@Foxinio
Copy link
Collaborator

Foxinio commented Nov 19, 2024

All the changes look good to me, however I did look at the problem mention above and there seems to exist simple (partial) fix. It will make record accessors non recursive when record is marked rec and when it is in rec block with only other records, data definitions and labels. It won't always make accessors pure, like in this piece of code:

rec
  let foo (x : T) (n : Int) =
    if n == 0 then x
    else foo (x.x n) (n - 1)

  data T = { x : Int -> T }
end

If we ever decide to make non-termination detection more precise, for example like the one that's present in coq, my change will become redundant as whole problem will be solved. Until then it will help with the not obscure code.

@Foxinio
Copy link
Collaborator

Foxinio commented Nov 19, 2024

changes mentioned:

diff --git a/src/DblParser/Desugar.ml b/src/DblParser/Desugar.ml
index 27d4fe2..ed8bcae 100644
--- a/src/DblParser/Desugar.ml
+++ b/src/DblParser/Desugar.ml
@@ -42,6 +42,19 @@ let tr_ctor_name' (cname : Raw.ctor_name) =
 
 let with_nowhere data = { pos = Position.nowhere; data = data}
 
+let rec node_is_rec_data (def : Raw.def) =
+  match def.data with
+  | DRecord _ -> true
+  | DData   _ -> true
+  | DLabel  _ -> true
+  | _ -> false
+
+let node_is_data_def (def : def) =
+  match def.data with
+  | DData  _ -> true
+  | DLabel _ -> true
+  | _ -> false
+
 let annot_tp e tp =
   { pos = e.pos;
     data = Raw.EAnnot(e, with_nowhere tp)
@@ -751,6 +764,11 @@ and tr_def ?(public=false) (def : Raw.def) =
   | DOpen(pub, path) -> 
     let public = public || pub in
     [ make (DOpen(public, path)) ]
+  | DRec(pub, defs) when List.for_all node_is_rec_data defs ->
+    let public = public || pub in
+    let dds, accessors = tr_defs ~public defs
+      |> List.partition node_is_data_def in
+    make (DRec dds) :: accessors
   | DRec(pub, defs) ->
     let public = public || pub in
     [ make (DRec (tr_defs ~public defs)) ]

Copy link
Member

@forell forell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't spotted anything suspicious, so I'm in favor of merging. However, as (I think) @ilikeheaps pointed out, we should make sure existing tests still make sense after the change. I remember you identified ok0079_impureMethod.fram as outdated, which tests the fix for an old bug reported in #14. We should force impurity in that test by some other means.

@ppolesiuk
Copy link
Member Author

@Foxinio These changes looks reasonable, but I think we should merge them in a separate pull-request. This PR propose basic solution to the problem of pure pattern-matching that has some limitations, but has no edge cases. Your fix improves the solution, but is not perfect, and in a long run will be probably replaced by something better. Having separate commits for them would make history cleaner, and it would be easier to revert your changes in order to provide the final solution.

@ppolesiuk ppolesiuk merged commit 556a62c into master Nov 19, 2024
2 checks passed
@ppolesiuk ppolesiuk deleted the 149-pure-pattern-matching branch November 19, 2024 17:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Pure pattern-matching
3 participants