Stream: Ltac2

Topic: Introducing in the context


view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 14:18):

I am trying to achieve the equivalent of intro in Ltac2, and I am failing. Here is one of my tries:

Std.intro None None;
let x :=
  lazy_match! reverse goal with
  | [ h : _ |- _ ] => h
  end in
...

I would expect x to contain an identifier that I could then use with &x. It does indeed contain an identifier from the context, but it is a random one. How can I get the one produced by Std.intro?

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:20):

x should contain the identifier, but beware that &x doesn't do what you believe it does. With this syntax it just looks for the hypothesis named with the literal x, not the bound variable x.

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:20):

Use Control.hyp to do that.

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:21):

Alternatively, why don't you provide an identifier to intro? You could generate it with the Fresh module.

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 14:22):

As I said in the other thread, I have a hard time recovering a useful ident.
Anyway, the documentation says: "&x as a Coq constr expression expands to ltac2:(Control.refine (fun () => hyp @x))." So, isn't that exactly your suggestion? What am I missing?

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:23):

@x means the literal ident x

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 14:23):

Oh!

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:24):

(we should probably have a shorter syntax for Control.hyp but that's another story that opens bikeshedding mode)

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 14:25):

So, if I understand correctly, I should never use & and @ in Ltac2 scripts, should I?

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:25):

Why not?

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:25):

You never use "foo" in ML?

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 14:27):

But how would I guess the actual literal name of a binder?

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:27):

you mean the stuff introduced by intros? tough luck on that one

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:28):

instead, you should 1. generate a fresh ident, 2. introduce it and 3. return it as a value

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:28):

like the infamously broken intro x; ... [some stuff mentioning x] in Ltac

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:28):

that is, define a tactic fresh_intro : unit -> ident that you can use this way

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 14:28):

So, yes, that is my point, I never have an identifier at hand I can use with & and @, unless I am missing something.

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:29):

it happens sometimes that the identifier is guaranteed to be preserved

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:29):

e.g. refine (fun x => ...) guarantees that the last binder is going to be x

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:29):

but yes, &x should mostly be reserved for inline proofs

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:30):

as for @ that's another story, if you want to generate a fresh name with a given prefix obviously you'll use that

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:30):

at least Ltac2 makes a difference between these concepts, and doesn't try at runtime to guess what you meant

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 14:33):

By the way, I am constantly writingltac2:(Control.refine (fun () => foo)). Is there some other way? If not, I would gladly have some ltac2val:(foo) to make it shorter.

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:33):

from Ltac1 you mean? or from a constr?

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 14:33):

From constr.

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:34):

there is the $x syntax

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 14:34):

Sorry, I meant that foo is a complex term. I was under the impression that $x only applies to an identifier.

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:35):

yes, in this case you should let-bind the term to an Ltac2 variable

view this post on Zulip Gaëtan Gilbert (Feb 03 2022 at 14:35):

how's that different from just foo?

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:35):

like let x := '(my complex term) in ... ($x) ...

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:36):

(assuming the term makes sense in the toplevel context)

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 14:36):

Unfortunately, I cannot. Because I am usually in a context where I cannot bind an Ltac2 variable, e.g., constr:(forall x, ltac2:(Control.refine (fun () => something that needs to see x))).

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:37):

you can always define your own notation, in this case

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:38):

I am not sure that it's a great idea to write this kind of terms with quotations

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:38):

it's currently lacking from the very barebone Ltac2 libraries but instead you should use some HOAS API

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:39):

so, e.g. a function forall_ : constr -> (constr -> constr) -> constr

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:39):

that one you can define it with quotations or low-level functions

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:39):

by the way, this is one case where you're guaranteed statically that x will keep its name

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 14:40):

inside a quotation, for any identifier the last introduced binder with this name is guaranteed to be preserved

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 14:54):

I am not sure HOAS would help here. The thing that needs to see x in the context are evars. So, I am not quite sure how I would even write the constr -> constr part of forall_.

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 14:56):

As I said in the other thread, I ended up writing a function evar that produces an evar with a given type. And this evar is only meaningful in a given context. So, I have things like:

Control.refine (fun _ => open_constr:(fun x : $t => _ : ltac2:(Control.refine (fun () => evar 'Prop))))

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 15:33):

not very pretty, please open PRs if you want to get closer to a 'batteries included' state

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 15:41):

Pierre-Marie Pédrot said:

e.g. refine (fun x => ...) guarantees that the last binder is going to be x

So, I just tried it, and unfortunately, it does not seem to be true. If I do the following:

Control.refine (fun () => open_constr:(fun x : nat => _ : (_ : Prop))) ;
Control.focus 1 1 (fun () =>
Message.print (Message.of_constr (Control.goal ())) ;
let y := constr:(eq &x) in
Control.refine (fun () => open_constr:(fun x : nat => _ : (_ : Prop))) ;
Control.focus 1 1 (fun () =>
Message.print (Message.of_constr (Control.goal ())) ;
let y := constr:($y &x) in
Message.print (Message.of_constr y))).

the final term being printed is x = x, while I would expect x0 = x. (The intermediate prints are just there to double-check that I am focusing the correct subgoals.)
So, I cannot rely on refine (fun x => ...) guaranteeing anything about the name of the last binder.
Am I missing something?

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 15:48):

that one is another issue (or call it a design choice if you want) which is that there has been a variable capture on an open var

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 15:48):

I didn't claim anything about that situation, btw

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 15:48):

my claim is about referring to the dynamic environment from within a quotation

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 15:49):

here you built a term eq x that refers to a context variable x but then you pull the rug under the feet of that term because you change the dynamic environment to another one

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 15:50):

it's your responsibility to check that the environment you're using the term in makes sense

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 15:50):

(you could get into way worse situations, e.g. clearing the variable or changing its type)

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 15:51):

That's another argument for HOAS, also, because it prevents this kind of situation (the renaming is implicit)

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 16:00):

Pierre-Marie Pédrot said:

here you built a term eq x that refers to a context variable x but then you pull the rug under the feet of that term because you change the dynamic environment to another one

I don't understand this part. The term eq x is already created. I am not trying to create it after the second refine.

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:01):

Yes, it's already created and it's just eq x where x is a name

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:01):

after the second refine, the last introduced binder is also x

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:01):

so you get eq x x

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:02):

the previous x was renamed into x0 in the dynamic context but we don't do anything for Ltac2 values

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:02):

(and in general, we wouldn't be able to)

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:04):

if instead of binding this term as an Ltac2 value, you had, say, posed it, you'd indeed see eq x0 x

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 16:08):

So, I am back to step one. I cannot trust &x. I have to manually create a fresh identifier.

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:08):

what do you want to do exactly?

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:10):

the original problem you raised at the beginning of the thread is solved IIUC

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 16:11):

It is not, because your suggestion of using Control.hyp x suffers from the same shadowing issue. So, I cannot use that.

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:11):

?

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:11):

you want to intro a variable

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:11):

you intro it, you look at its name, poof

view this post on Zulip Gaëtan Gilbert (Feb 03 2022 at 16:12):

So, I just tried it, and unfortunately, it does not seem to be true. If I do the following:

ah it's wellknown: ltac variable bypasses typechecking again
but with ltac2 instead

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:12):

Ltac2 is no more evolved than Ltac1 in that regard

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:12):

it inherits the same low-level code

view this post on Zulip Guillaume Melquiond (Feb 03 2022 at 16:13):

Pierre-Marie Pédrot said:

you intro it, you look at its name, poof

But its name is always x. So, whichever term I create using it sooner or later becomes invalid.

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:13):

well, if you escape the context yes

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:14):

if you do this in a well-scoped way, without any clear and generating a fresh ident at every intro you'll be fine

view this post on Zulip Pierre-Marie Pédrot (Feb 03 2022 at 16:15):

whichever term I create using it sooner or later becomes invalid.

terms within a context do have a shelf life, and that's this context


Last updated: Sep 09 2024 at 05:02 UTC