Zulip Chat Archive

Stream: lean4

Topic: Syntax -> Name


Henrik Böving (Mar 19 2022 at 21:10):

I'd like to build a macro that looks somewhat like this:

macro "(" x:ident "=" y:term ")" : term => `(λ name => if $x == name then $y else 0)

but x should be turned into a Name instead of being an identifier, I did find the function. I did find getNameOfIdent' which looks like what I need but its only used in more involved elaborators so I'm not quite sure how to use it in a macro case, can i even? If not how can I achieve what i want?

Simon Hudon (Mar 19 2022 at 21:24):

use x.getId

Henrik Böving (Mar 19 2022 at 21:34):

How exactly?

macro "(" x:ident "=" y:term ")" : term => `(λ name => if x.getId == name then $y else 0)

#check (x = 12)

didnt work

Henrik Böving (Mar 19 2022 at 21:39):

Also since this syntax will probably bite me sooner or later I did try other delimiters (namely <) but those ended up colliding with other syntax as well :/ Would someone happen to have better suggestion for syntax like this?

Simon Hudon (Mar 19 2022 at 21:42):

Can you show the error message that you get?

Simon Hudon (Mar 19 2022 at 21:44):

Also, sorry for the lack of details: this should work better (modulo the right choice of concrete syntax):

`(λ name => if $(Lean.Syntax.mkNameLit x.getId) == name then $y else 0)

Henrik Böving (Mar 19 2022 at 21:52):

Simon Hudon said:

Can you show the error message that you get?

The error is that it tells me on the next line its expecting a term, I susepect this is because it might be expecting a greater than notation at the end? not sure.

Simon Hudon said:

Also, sorry for the lack of details: this should work better (modulo the right choice of concrete syntax):

`(λ name => if $(Lean.Syntax.mkNameLit x.getId) == name then $y else 0)

Yes it diiid work better but mkNameLit takes a String so I did a .toString on the getIdent which resulted in:

macro "(" x:ident "=" y:term ")" : term => `(λ name => if $(Lean.Syntax.mkNameLit x.getId.toString) == name then $y else 0)

#check (x = 12)

but this in turn still gives me a:

overloaded, errors
  elaboration function for 'nameLit' has not been implemented
    failed to pretty print term (use 'set_option pp.rawOnError true' for raw representation)

  25:8 unknown identifier 'x'

:/

Sebastian Ullrich (Mar 19 2022 at 22:11):

It's not clear to me what your macro is supposed to do. Do you really want (x = 12) : Name -> Nat?

Sebastian Ullrich (Mar 19 2022 at 22:14):

In any case, you'll want quote x.getId

Henrik Böving (Mar 19 2022 at 22:37):

Sebastian Ullrich said:

It's not clear to me what your macro is supposed to do. Do you really want (x = 12) : Name -> Nat?

Yes, the original syntax I was aiming for was <x = 12> : Name -> Nat but the parser doesnt seem to like that

Henrik Böving (Mar 19 2022 at 22:38):

But the quote part did work out thank you!

Mario Carneiro (Mar 20 2022 at 01:47):

Henrik Böving said:

Sebastian Ullrich said:

It's not clear to me what your macro is supposed to do. Do you really want (x = 12) : Name -> Nat?

Yes, the original syntax I was aiming for was <x = 12> : Name -> Nat but the parser doesnt seem to like that

Umm, (x = 12) isn't really better. That syntax already means something, are you going to break all uses of = in parentheses?

Henrik Böving (Mar 20 2022 at 12:03):

I'm very much aware of that, I just don't know of another alternative /o\ feel free to give me one

Mario Carneiro (Mar 20 2022 at 12:40):

what is the context? This looks like an #xy problem

Henrik Böving (Mar 20 2022 at 12:42):

I'm mirroring https://isabelle.in.tum.de/library/HOL/HOL-IMP/AExp.html and they defined the syntax <''y'' := 7> so I tried to mirror that (without the '' around names, i also attempted := instead of= but that came down to the same) and this is what came out of it

Mario Carneiro (Mar 20 2022 at 12:52):

I recommend using a dedicated syntax category for your expressions, with a macro introducer syntax like imp { x := 7 }

Henrik Böving (Mar 20 2022 at 12:52):

Ah that does sound reasonable yes, thank you!

Mario Carneiro (Mar 20 2022 at 12:53):

also it should not expand to that if statement, it should be a definition like assign "x" (const 7)

Henrik Böving (Mar 20 2022 at 12:54):

Why's that?

Mario Carneiro (Mar 20 2022 at 12:54):

because that way you can have appropriate simp lemmas and theorems about the definition

Mario Carneiro (Mar 20 2022 at 12:55):

if you write a big program then using notations will lead to an unnecessarily large term

Mario Carneiro (Mar 20 2022 at 12:55):

also delab doesn't work as well on such expressions

Henrik Böving (Mar 20 2022 at 12:55):

I see

Henrik Böving (Mar 20 2022 at 12:58):

Oh also, I did notice that they use this blast tactic quite a lot (it's a generic tableau prover) do we have such a thing as well (maybe in Lean 3?) would it be useful for us if not?

Mario Carneiro (Mar 20 2022 at 13:08):

there is eblast in lean 3, but it is not used much because it has bugs and no maintainer. It would certainly be good to have a FOL prover in lean, it is not something that we handle well

Henrik Böving (Mar 20 2022 at 13:08):

puts that on the list of things to have a look at at some point

Mario Carneiro (Mar 20 2022 at 13:09):

I believe aesop will address some of the needs in this domain, although that is closer to auto than blast

Henrik Böving (Mar 20 2022 at 13:15):

Henrik Böving said:

puts that on the list of things to have a look at at some point

scratch that I'm just writing endless stacks of cases and apply here, I'll look into it right away

Mario Carneiro (Mar 20 2022 at 13:38):

did you try simp? It is probably selection bias but I don't really find a strong need for a blast like tactic most of the time

Henrik Böving (Mar 20 2022 at 13:45):

I did @[simp] my declarations yeah, the thing is that my proofs right now basically look like:

  apply Iff.intro
  case mp =>
    intro h
    cases h with
    | Seq h1 h2 =>
      cases h1 with
      | Seq h3 h4 =>
        apply Seq h3
        apply Seq h4
        assumption
  case mpr =>
    intro h
    cases h with
    | Seq h1 h2 =>
      cases h2 with
      | Seq h3 h4 =>
        apply Seq
        apply Seq h1
        repeat {assumption}

and simp doesnt seem to be able to apply cases on its own. But this proof would definitely be blast-able if blast was told about the cases as eleminiation rule and the Seq as intro rule since its just a tableau like this.

Mario Carneiro (Mar 20 2022 at 13:46):

that looks like And.assoc

Mario Carneiro (Mar 20 2022 at 13:48):

and you can probably write the proof as ⟨fun ⟨⟨a, b⟩, c⟩ => ⟨a, ⟨b, c⟩⟩, fun ⟨a, ⟨b, c⟩⟩ => ⟨⟨a, b⟩, c⟩⟩

Henrik Böving (Mar 20 2022 at 13:48):

lets see

Henrik Böving (Mar 20 2022 at 13:50):

Ah, no I cannot (but that's just because I didn't show the big picture here) I'm now in this theory https://isabelle.in.tum.de/library/HOL/HOL-IMP/Big_Step.html at Seq_assoc so whenever I apply Seq I can't just use the anonymous constructor.

Henrik Böving (Mar 20 2022 at 13:53):

If you move further down the file you'll also see that they are just able to blast most of the semantic equivalence goals

Henrik Böving (Mar 20 2022 at 16:54):

@Mario Carneiro do we have automation for such things with simp as well? Or does this require something new then?

Henrik Böving (Mar 20 2022 at 16:54):

I'm guessing the 2nd?

Mario Carneiro (Mar 20 2022 at 16:55):

You haven't shown a MWE so I don't know whether it can be done better. I'm just making guesses based on your code snippet

Henrik Böving (Mar 20 2022 at 17:02):

That would be:

-- Everything until BigStep is just setup
inductive AExp where
| N : Int  AExp
| V : String  AExp
| Plus : AExp  AExp  AExp

abbrev State := String  Int

namespace AExp
declare_syntax_cat aexp

syntax num : aexp
syntax "-" num : aexp
syntax str : aexp
syntax aexp "+" aexp : aexp
syntax "(" aexp ")" : aexp
syntax term : aexp

syntax "[AExp|" aexp "]" : term

macro_rules
| `([AExp| $num:numLit ]) => `(AExp.N $num)
| `([AExp| - $num:numLit ]) => `(AExp.N (-$num))
| `([AExp| $x:strLit ]) => `(AExp.V $x)
| `([AExp| $l + $r]) => `(AExp.Plus [AExp| $l] [AExp| $r])
| `([AExp| ( $x )]) => `([AExp| $x])
| `([AExp| $x:term ]) => `($x)

def aval : AExp  State  Int
| N n, s => n
| V n, s => s n
| Plus l r, s => (aval l s) + (aval r s)

def updateState (s : State) (n : String) (v : Int) : State := λ x => if x == n then v else s x

end AExp

inductive BExp where
| BConstant : Bool  BExp
| Not : BExp  BExp
| And : BExp  BExp  BExp
| Less : AExp  AExp  BExp

namespace BExp
open AExp

declare_syntax_cat bexp
syntax "⊤" : bexp
syntax "⊥" : bexp
syntax "¬" bexp : bexp
syntax aexp "<" aexp : bexp
syntax "(" bexp ")" : bexp
syntax term : bexp

syntax "[BExp|" bexp "]" : term

macro_rules
| `([BExp|  ]) => `(BExp.BConstant Bool.true)
| `([BExp|  ]) => `(BExp.BConstant Bool.false)
| `([BExp| ¬ $x]) => `(BExp.Not $x)
| `([BExp| $l:aexp < $r:aexp]) => `(BExp.Less [AExp| $l] [AExp| $r])
| `([BExp| ( $x )]) => `([BExp| $x])
| `([BExp| $x:term ]) => `($x)

def bval : BExp  State  Bool
| BConstant b, s => b
| Not b, s => !(bval b s)
| And l r, s => bval l s && bval r s
| Less l r, s => aval l s < aval r s

inductive Com where
| Skip : Com
| Assign : String  AExp  Com
| Seq : Com  Com  Com
| If : BExp  Com  Com  Com
| While : BExpr  Com  Com

declare_syntax_cat com

syntax "SKIP" : com
syntax str ":=" aexp : com
syntax term ":=" aexp : com
syntax com ";" com : com
syntax "if" bexp "exec" com "else" com : com
syntax "while" bexp "exec" com: com
syntax "(" com ")" : com
syntax term : com

syntax "[Com|" com "]" : term

macro_rules
| `([Com| SKIP]) => `(Com.Skip)
| `([Com| $x:strLit := $y ]) => `(Com.Assign $x [AExp| $y])
| `([Com| $x:term := $y ]) => `(Com.Assign $x [AExp| $y])
| `([Com| $x ; $y]) => `(Com.Seq [Com| $x] [Com| $y])
| `([Com| if $b exec $l else $r]) => `(Com.If [BExp| $b] [Com| $l] [Com| $r])
| `([Com| while $b exec $x]) => `(Com.While [BExp| $b] [Com| $x])
| `([Com| ( $x:com )]) => `([Com| $x])
| `([Com| $x:term ]) => `($x)

inductive BigStep : Com  State  State  Prop where
| Skip : BigStep Skip s s
| Assign : BigStep [Com| x := a] s (updateState s x (aval a s))
| Seq (h1 : BigStep c1 s1 s2) (h2 : BigStep c2 s2 s3) : BigStep [Com| c1;c2] s1 s3
| IfTrue (h1 : bval b s1) (h2 : BigStep c1 s1 s2) : BigStep [Com| if b exec c1 else c2] s1 s2
| IfFalse (h1 : ¬bval b s1) (h2 : BigStep c2 s1 s2) : BigStep [Com| if b exec c1 else c2] s1 s2
| WhileFalse (h1 : ¬bval b s) : BigStep [Com| while b exec c] s s
| WhileTrue (h1 : bval b s1) (h2 : BigStep c s1 s2) (h3 : BigStep [Com| while b exec c] s2 s3) : BigStep [Com| while b exec c] s1 s3

notation "(" c "," s ")" "→" t => BigStep c s t

theorem seq_assoc : (([Com| (c1;c2);c3], s)  s')  (([Com| c1;c2;c3], s)  s') := sorry

...one can probably ignore the syntax stuff for the most part, its really about the BigStep inductive and the theorem

Henrik Böving (Mar 20 2022 at 17:04):

It basically comes down to "Do we have good support for automatic reasoning on inductive predicates"

Henrik Böving (Mar 20 2022 at 17:07):

inductive Com where
| Skip : Com
| Seq : Com  Com  Com

declare_syntax_cat com

syntax "SKIP" : com
syntax com ";" com : com
syntax "(" com ")" : com
syntax term : com

syntax "[Com|" com "]" : term

macro_rules
| `([Com| SKIP]) => `(Com.Skip)
| `([Com| $x ; $y]) => `(Com.Seq [Com| $x] [Com| $y])
| `([Com| ( $x:com )]) => `([Com| $x])
| `([Com| $x:term ]) => `($x)

inductive BigStep : Com  State  State  Prop where
| Skip : BigStep Skip s s
| Seq (h1 : BigStep c1 s1 s2) (h2 : BigStep c2 s2 s3) : BigStep [Com| c1;c2] s1 s3

notation "(" c "," s ")" "→" t => BigStep c s t

theorem seq_assoc : (([Com| (c1;c2);c3], s)  s')  (([Com| c1;c2;c3], s)  s') := sorry

I guess this one would be more minimal xd

Henrik Böving (Mar 20 2022 at 17:11):

Hmm...actually removing things did change apparently change stuff so the original proof doesnt work anymore :/

Mario Carneiro (Mar 20 2022 at 17:13):

The Skip case looks wrong; it appears red in the definition of BigStep because it's actually a variable

Henrik Böving (Mar 20 2022 at 17:14):

Now it works, it shouldve bee Com.Skip yeah..

Mario Carneiro (Mar 20 2022 at 17:16):

inductive BigStep : Com  State  State  Prop where
| skip : BigStep Com.Skip s s
| assign : BigStep [Com| x := a] s (updateState s x (aval a s))
| seq (h1 : BigStep c1 s1 s2) (h2 : BigStep c2 s2 s3) : BigStep [Com| c1;c2] s1 s3
| if_true (h1 : bval b s1) (h2 : BigStep c1 s1 s2) : BigStep [Com| if b exec c1 else c2] s1 s2
| if_false (h1 : ¬bval b s1) (h2 : BigStep c2 s1 s2) : BigStep [Com| if b exec c1 else c2] s1 s2
| while_false (h1 : ¬bval b s) : BigStep [Com| while b exec c] s s
| while_true (h1 : bval b s1) (h2 : BigStep c s1 s2) (h3 : BigStep [Com| while b exec c] s2 s3) : BigStep [Com| while b exec c] s1 s3

notation "(" c ", " s ")" " → " t => BigStep c s t

theorem BigStep.seq_assoc : (([Com| (c1;c2);c3], s)  s')  (([Com| c1;c2;c3], s)  s') :=
  fun | seq (seq h1 h2) h3 => seq h1 (seq h2 h3),
   fun | seq h1 (seq h2 h3) => seq (seq h1 h2) h3

Henrik Böving (Mar 20 2022 at 17:28):

That does indeed look much better yes...but judging from this the answer to my question seems that we don't really have good automation for constructing these derivation trees automatically, just nice syntax to denote them right?

Mario Carneiro (Mar 20 2022 at 17:48):

Well, it would be different if you used and / or / eq instead of a custom inductive type

Henrik Böving (Mar 20 2022 at 17:55):

Yeah I get that...but getting that to work is exactly the point of Isabelle's blast since it's not just a FOL prover but a generic tableau prover.

Henrik Böving (Mar 20 2022 at 17:55):

Anyways, thanks for all the help and patience!

Jannis Limperg (Mar 30 2022 at 14:45):

FYI Aesop now solves this (with some appropriately tagged lemmas):

import Aesop

abbrev State := String  Int

inductive Com where
| Skip : Com
| Seq : Com  Com  Com

declare_syntax_cat com

syntax "SKIP" : com
syntax com ";" com : com
syntax "(" com ")" : com
syntax term : com

syntax "[Com|" com "]" : term

macro_rules
| `([Com| SKIP]) => `(Com.Skip)
| `([Com| $x ; $y]) => `(Com.Seq [Com| $x] [Com| $y])
| `([Com| ( $x:com )]) => `([Com| $x])
| `([Com| $x:term ]) => `($x)

@[aesop safe]
inductive BigStep : Com  State  State  Prop where
| Skip : BigStep Com.Skip s s
| Seq (h1 : BigStep c₁ s t) (h2 : BigStep c₂ t u) : BigStep [Com| c₁;c₂] s u

namespace BigStep

@[aesop norm elim]
theorem Seq_inv (h : BigStep [Com| c₁;c₂] s u) :
     t, BigStep c₁ s t  BigStep c₂ t u :=
  match h with
  | Seq (t := t) h₁ h₂ => t, h₁, h₂

end BigStep

theorem seq_assoc :
    BigStep [Com| (c1;c2);c3] s s'  BigStep [Com| c1;c2;c3] s s' := by
  aesop

Henrik Böving (Mar 30 2022 at 16:59):

Yay more concrete semantics for me!


Last updated: Dec 20 2023 at 11:08 UTC