I am observing a discrepancy between Retyping.relevance_of_type
on template polymorphic and properly polymorphic/monomorphic inductives applied to unbound Rel
s. I am using Ltac2 to trigger this but the trace shows pretty clearly that it only really concerns Retyping.relevance_of_type
and nothing Ltac2-specific.
Inductive tmplpoly := .
Fail Ltac2 Eval Binder.make None (make (appL '@tmplpoly [make (Rel 1)])).
(* Uncaught exception Retyping.RetypeError(0) *)
#[universes(polymorphic)] Inductive poly (T:Type) := .
Ltac2 Eval Binder.make None (make (appL '@poly [make (Rel 1)])). (* Works fine. *)
#[universes(monomorphic)] Inductive mono (T:Type) := .
Ltac2 Eval Binder.make None (make (appL '@mono [make (Rel 1)])). (* Works fine. *)
I suspect this is a bug but I don't know what to expect from this. My code currently relies on Binder.make
accepting terms with unbound Rel
s so to me this a bug with template polymorphic inductives. But I am wondering now how one can determine the relevance of an unbound Rel
.
we could make this one work, but in general unbund rels won't work out well
Should I create my own alternative Binder.make
version that always picks Relevant
? I see that the docs mention caveats there but we don't (yet) use SProp so this might be OK for now. The alternative fix is to always extend the local environment when working under binders. That can be quite annoying in Ltac2 but maybe I just need to write some new primitives for that.
How do plugins deal with the choice of relevance when generating binders? I doubt everybody writing plugin code has a solid strategy for supporting SProp, or do they?
nope, and that's why using SProp is currently a hell
Hmm
most of the code was ported setting Relevant everywhere, which is somewhat sensible but not very correct
the net result is that when you use SProp you get weird conversion errors here and there between terms that are literally the same
About Ltac2, we should probably provide primitives similar to the ones we have in OCaml to manipulate terms under contexts
I wonder now if maybe Ltac2 is doing the wrong thing by trying to infer relevance for binders. Why not let the user pick something? Because it seems in general the answer will have to be Relevant
anyway, especially when the types involved have unbound Rel
s in them.
I think that this reflects the poor API we have currently on the OCaml side, we're erring between different design choices and the current situation is not very satisfactory
it's very unsafe to let the user pick whatever they want, so it makes sense to have something automatic by default
there used to be an explicit API in Ltac2 as well, so everything is moving everywhere
Pierre-Marie Pédrot said:
About Ltac2, we should probably provide primitives similar to the ones we have in OCaml to manipulate terms under contexts
What kind of primitives are those? I've always thought Mtac's \nu
operator was very intuitive. It's a scoped addition to the context. For some reason it does named variables only but I don't see why something like that wouldn't work for Rel
s.
we have map / fold under contexts notably
I am open to proposals for extending the Ltac2 API in a palatable way, btw
also the OCaml representation of relevance is bound (no pun intended) to change soon
Do these primitives extend the local environment so that e.g. Retyping.relevance_of_type
would work correctly when working under binders?
not in OCaml but we could do that in Ltac2
(because there is no such thing as "the local environment" in OCaml)
Everything needs an env
to function correctly, doesn't it? That's what I call the local environment. A bit of a misnomer I guess
not necessarily, and there are several notions of environment in the tactic monad
Ah, yes, I have suffered from picking the wrong environment. There is the one of the monad and the one of the current goal, at least, I think.
the ones of the the current goals
:)
I am not sure if this just me but I see a mismatch here between the ability to unsafely create terms and the reliance on the implicit local environment on the Ltac2 side. If am free to create unbound Rel
s, then I really want all the rest of the API to be as resilient as possible, right?
Maybe this is some kind of inherent tension between Ltac2 the tactic language and Ltac2 the metaprogramming language
it just means we need two kinds of APIs
one for manipulating raw untyped terms, very unsafe and you know what you're doing
and another one for the casual user that wants high-level operators
thankfully we have functions rather than hardwired notations in Ltac2, so it's just a matter of exposing that
That does sound reasonable to me
(you could even implement the high level one through the low-level one, if you wanted so)
It also suggests that my short-term fix will be exposing a version of Binder.make
that either hardcodes Relevant
or lets me copy the relevance from an existing binder (which would cover most of my use cases.)
just expose the low-level binder making function that takes an explicit relevance, as it used to be
you might simply revert the commit that removed it...
That commit also bundled the type of the binder into the binder
type itself so reverting it would be a bit chaotic. In any case, I won't patch Ltac2 itself but I'll expose these old binder
functions. It's much easier to get people to recompile our local development than it is to get them to recompile Coq (really just Ltac2 but it's the same opam package IIRC)
BTW is relevance only needed when binding (S : SProp) or members of an SProp? Or also when binding datatypes that contain SProp?
for binding x : P : SProp
Last updated: Jun 09 2023 at 07:01 UTC