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

Simplify interp/constrintern.ml:sort_fields #86

Closed
wants to merge 8 commits into from

Conversation

gasche
Copy link
Contributor

@gasche gasche commented Jun 29, 2015

Background: at the end of the Coq Coding Sprint, I decided that a feature that would be within reach (unlike fun <pattern> =>...) would be the ability to define records by reusing fields of an existing records, that is {| r with f1 = ...; f2 = ... |}.

The function to use for this purpose is sort_fields in interp/constrintern.ml. Unfortunately it is undocumented and its code was unclear enough that I could not understand what it does. (There was a boolean argument named mode and a local variable named boolean, for example.)

The current pull-request brings zero new feature, but is a series of simplification and changes to the code of sort_fields that allowed me to understand its semantics, culminating in a documentation comment. I suspect the code changes along the way could also help future readers, which is why I didn't just include the comment in the pull request.

Note 1: it's quite hard to review all changes at once, because there are a many simultaneous changes going on. I would recommend looking at the commits one by one, I tried to make them as atomic as reasonably possible.

Note 2: there is a question in a commend prefixed by (* G.S.:, if someone can answer it I think inline comments would be a good place to discuss it, otherwise I'll just remove the comment.

                         (* G.S.: why do we fail only in the
                            first-field case? I would expect to fail
                            whenever (not regular && complete), and
                            skip the fields only when (not complete *)

Note 3: now that I do understand what the function does, I suspect there should be a more direct way to implement it (than the inherently quadratic processing being performed here), but I resisted the temptation to do it. My point was just to make the existing implementation clearer.

Note 4: I did a couple mistakes during my changes, and observed that Coq immediately fails to build theories/Logic/ExtensionalityFacts.vo. I therefore suspect that Coq breaks in obvious way when this function is bugged, which gives me confidence that the new implementation is as bug-free as the previous one.

Note 5: I was a bit frightened when I discovered that make test-suite fails miserably in my feature branch, but then found out that it already fails on trunk with the same failures (included below), so I guess that is ok.

    success/decl_mode2.v...Error! (should be accepted)
    bugs/opened/3948.v...Error! (bug seems to be closed, please check)
    bugs/opened/4214.v...Error! (bug seems to be closed, please check)
    output/inference.v...Error! (unexpected output)
    output/Notations.v...Error! (unexpected output)

gasche added 7 commits June 26, 2015 21:54
Note that turning
  let boolean = not regular in
  if boolean && complete then ...;
  if boolean && complete then ...;
into
  if not regular && complete then ...;
  if not regular && complete then ...;
has absolutely no performance cost: negation inside a conditional is
not computed as a boolean, it only flips the branches. The code is
more readable because "boolean" was a terrible variable name.
The code was a big "try..with" defining all useful quantities at
once. I tried to lift definitions out of this try..with to define them
as early as possible: the record's information and the first field
name are fetched before processing the other fields.

There were two calls in the try..with body that could raise the
Not_found exception (or at least I don't know the code well enough to
be sure that either of them cannot): `shortest_qualid_of_global` and
`build_patt`. They are now split in two separate try..with blocks,
both raising the same exception (with a shared error message named
`env_error_msg`). Someone familiar with the invariants at play could
probably remove one of the two blocks, streamlining the code even
further.

I'm a bit surprised by the main logic part (the big (if .. else if
.. else if ..) block in the new code), and there is a question in
a comment. I hope to get it answered during code review and remove it
(and maybe simplify the code).

Finally, there was an apparently-stale comment in the code:
  (* insertion of Constextern.reference_global *)
of course Constextern.reference_global corresponds to now function
that I could find. After trying to understand the meaning of this
comment, I decided to just remove it.
we already have
  val remove_first : ('a -> bool) -> 'a list -> 'a list
  (** Remove the first element satisfying a predicate, or raise [Not_found] *)
now we also have the more general
  val extract_first : ('a -> bool) -> 'a list -> 'a list * 'a
  (** Remove and return the first element satisfying a predicate,
      or raise [Not_found] *)

The implementation is tail-recursive. The code I'm hoping to factorize
reimplements extract_first in a tail-recursive way, so it seemed good
to preserve this. On the other hand remove_first is not tail-recursive
itself, and that gives better constant factors in real-life
cases. It's unclear what is the best choice.
The internal `add_pat` function is replaced by a call to
`CList.extract_first`.
The type of the user-defined function "completer" changes to be
simpler and better reflect its purpose: provide values for missing
field assignments. In the future we may want to also pass the name of
the field as parameter (currently only the index is given, and both
uses of the function ignore it), in particular if we want to implement
{ r with x = ...; y = ... }.
(Because the function is private to the module, it is documented in
the .ml rather than the .mli)
@gasche
Copy link
Contributor Author

gasche commented Jun 30, 2015

Hugo orally clarified my question-in-comments this morning: non-regular fields are defined as let-bindings in the record definition itself, so their value is fixed from the other fields. In particular, it does not make sense to provide their values at record construction time. I do think it is the case that the not regular && complete case should always be rejected.

Some thing I still don't understand is whether the completer function can be called when building record expressions (in terms of the new code, when complete is true). The sort_fields call used to build expressions plugs evars in the missing fields, but it is not clear to me whether it could ever happen (won't the function definition fail in this case?).

@maximedenes
Copy link
Member

test build

@mattam82
Copy link
Member

mattam82 commented May 3, 2016

@gasche The holes could be for typeclass instances, or turned into obligations by Program or subgoals by refine for example.
About the [not regular && complete] case, shouldn't these projections simply be skipped as you do now? Why fail there, when these projection values cannot be specified for patterns or constructed terms?

@maximedenes
Copy link
Member

Merged, at last. Thanks a lot for this patch and sorry for the delay to merge it. We have become more reactive on pull requests since then :)

@gasche
Copy link
Contributor Author

gasche commented Jun 27, 2016

@maximedenes no harm done on my side, this is not a feature-implementing patch anyway, and for me the value was more in the making (getting to understand this corner of Coq better) than in the integration itself. But hopefully it will pave the way for more record-related ideas, possibly going back to that {| foo with ... |} syntax.

Thanks!

maximedenes pushed a commit to maximedenes/coq that referenced this pull request Apr 25, 2019
proux01 pushed a commit to proux01/coq that referenced this pull request Sep 10, 2024
Rename the files holding the news
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.

3 participants