-
Notifications
You must be signed in to change notification settings - Fork 34
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
Named global fixpoints and cofixpoints #45
base: master
Are you sure you want to change the base?
Conversation
One detail to be analyzed is the one of termination checking. In particular it should be made more tolerant on the result of lambda lifting. For example Another detail is very related to a recent PR of yours about fixpoints with no rec call. With global fixpoints one can define "hard" projections as non recursive fixpoints. It is useful because the reduction behavior is what one really expect from a projection (a bit like primproj, but without the optimization of parameters). |
We should definitely apply this to Coq.
Wouldn't it be a bit hackish? I mean: the need for triggering a delta only under some specific restrictions of normalization of the arguments is a perfectly legitimate need, as the case of projections show. But I would suggest to give to this need its full value rather than artificically declaring non-recursive functions as recursive just because only fixpoints (currently) support these restrictions. This being said, I don't know how to proceed. Do you think it would be worth to write another CEP on each of these two points? |
I'd leave more time to others to comment, and try to give a more detailed roadmap with intermediate milestones. It seems a large change for just one PR, it may be reasonable to foresee intermediate "releasable/mergeable" status of the feature. |
I would avoid having CEPs depending on other CEPs. You can definitely have a CEP describing a long plan that should be implemented in many PRs. |
text/global-fixpoints.md
Outdated
|
||
The objective of this Coq enhancement proposal is to provide "natively" named fixpoints and cofixpoints whose reduction rules directly produce what we intuitively expect, i.e. in, the case (*) below, directly go from `add (S t) m` to `S (add t) m`. | ||
|
||
The idea is well-known and used in other proof assistants (e.g. Adga): it is that the body of the fixpoint or cofixpoint refers to the global name associated to the fixpoint rather than to the root of the anonymous `fix` or `cofix` expression. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW: while this change sounds good, AFAIK Agda (not Adga) goes further, like Equations: the only reduction rules are for add (S m) n -> S (add m) n
and add 0 n -> n
. Anything else is neutral (including AFAIK add
itself; even their analogue of the match
construct is derived by lambda-lifting to the top-level). That might not be flexible enough for Coq, but in this common case reduction Just Works (tm) out of the box. But I've just used it, not investigated the relevant metatheory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But to be sure — this comment probably amounts to "here's a possible research direction, which I'm not fully qualified to judge". EDIT: moreover agda chooses to make top-level names generative, which (as you say) would create additional incompatibilities.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The basic question is whether you want to have a new world with a new add
in parallel to the current add
or if you want to extend the current add
. In general, I tend to be on the side of preserving the existing with minimal changes, so that we can continue to benefit of all what has been done so far.
PS: typo fixed, thanks.
ee0bc00
to
0786487
Compare
After having made some experiment to implement the proposal, I arrived to the conclusion that this was a wrong direction. A much simpler solution would just be to add an extra field to Fix/CoFix terms that tells how to "refold" the term after expansion. E.g., internally, instead of: Fix (([|0|], 0), ([|(Name add)|], [|nat->nat->nat|], [|... body ...|]) we would have Fix (([|0|], 0), ([|(Name add, Some Nat.add)|], [|nat->nat->nat|], [|... body ...|] so that, at reduction time, we can expand by substituting the body not with the Kernel-wise, to accept the extra Note that the extra optional information has to be a term rather than just a constant because the general form for a constant is to be a sequence of Remark: it is unclear whether this would be enough to provide |
but when the kernel sees the definition of Nat.add, Nat.add does not yet exist. How is that checked? |
Good question. For defining This seems similar to what happens in programming languages where we prepare a slot beforehand for the recursive definition in the environment and eventually "tie the loop". |
Perhaps, it is a bad idea to store the term in the original definition. It might be better for the kernel to just create it on the fly when unfolding a definition with a non-empty stack. For example, if the kernel decides to unfold |
I would say that what you describe is what |
I think making the info optional is better, since qed type checking could just behave as today, while the upper layers could use this extra datum to implement refolding. Lazy could be parametrized on this. It would have the status of a pretty printing hint, lake the names on binders. |
Rendered