Zulip Chat Archive

Stream: lean4

Topic: Extending do notation with `+=`


Tomas Skrivan (Apr 04 2022 at 13:00):

I would like to extend do notation with x += 1, x[0] := 1, x[0] += 1 and alike.

I have figured out how to extend the parser, but I do not know how to correctly follow up with elaboration.

import Lean.Elab.Term
import Lean.Parser.Term
import Lean.Parser.Do

open Lean Parser Term Elab

@[doElemParser] def Lean.Term.Parser.doFoo := leading_parser "foo"

#eval Id.run do
  foo -- Error: unexpected do-element of kind Lean.Term.Parser.doFoo: foo

def letAddDeclNoBinders := node `Lean.Parser.Term.letIdDecl $ atomic (Lean.Parser.Term.ident >> pushNone >> optType >> " += ") >> termParser
@[doElemParser] def Lean.Term.Parser.doAddReassign := leading_parser notFollowedByRedefinedTermToken >> letAddDeclNoBinders

-- This should be called in Lean.Elab.Term.Do.ToCodeBlock.doSeqToCode
def getDoAddReassignVars (doReassign : Syntax) : TermElabM (Array Name) := sorry

#eval Id.run do
  let mut x := 0
  x += 1 -- Error: unexpected do-element of kind Lean.Term.Parser.doAddReassign: x += 1
  x

The code is failing inside of doSeqToCode . There does not seem to be a path for a custom do-element :( How can I fix it?

Mario Carneiro (Apr 04 2022 at 13:01):

you could try handling it as a macro instead of an elab

Mario Carneiro (Apr 04 2022 at 13:08):

syntax atomic(ident Term.optType) " += " term : doElem
macro_rules
| `(doElem| $x:ident $[: $ty]? += $e) => `(doElem| $x:ident $[: $ty]? := $x:ident + $e)

#eval Id.run do
  let mut x := 0
  x += 1
  pure x

Tomas Skrivan (Apr 04 2022 at 13:11):

Thanks a lot! Definitely a better approach. I was trying to do lots of different things with syntax and macro_rules, but I have always failed to get anywhere.

Mario Carneiro (Apr 04 2022 at 13:20):

it is still limited in that you have to produce a doSeq syntax at the end of the day, there is no equivalent of elab for such things

Mario Carneiro (Apr 04 2022 at 13:20):

but for += that's probably fine

Tomas Skrivan (Apr 04 2022 at 13:23):

This is nice, x[0] := 42 is working too!

syntax atomic(Lean.Parser.Term.ident) noWs "[" term "] := " term : doElem

macro_rules
| `(doElem| $x:ident[ $i:term ] := $xi) => `(doElem| $x:ident := ($x:ident).set! $i $xi)

#eval Id.run do
  let mut x : Array Nat := #[0,1,2,3]
  x[0] := 42
  x

There is one problem that there has to be exactly one space between x[0] and :=. Replacing "] := " with "]" " := " in the syntax definition complains about multiple interpretations.

Mario Carneiro (Apr 04 2022 at 13:25):

yes, because lean already defines this syntax

Mario Carneiro (Apr 04 2022 at 13:25):

I think it is a TODO right now though

Mario Carneiro (Apr 04 2022 at 13:26):

you can make your syntax higher priority

Tomas Skrivan (Apr 04 2022 at 13:27):

Mario Carneiro said:

yes, because lean already defines this syntax

Ohh, is the notation x[0] := 42 new? I though that it is not possible.

Tomas Skrivan (Apr 04 2022 at 13:34):

And how can I find the definition of the built in x[0] := 42 syntax? So I can make sure my types are supporting it.

Mario Carneiro (Apr 04 2022 at 14:46):

I take it back, there is no special support in this particular position. However the syntax term := term is accepted because the left term can be a pattern, and x[0] := 42 falls into this

Tomas Skrivan (Apr 04 2022 at 16:35):

As you have mentioned, changing priority solves the problem:

import Lean.Parser.Term

open Lean Parser Term

syntax (priority := high) atomic(Term.ident) noWs "[" term "]" " := " term : doElem

macro_rules
| `(doElem| $x:ident[ $i:term ] := $xi) => `(doElem| $x:ident := ($x:ident).set! $i $xi)

#eval Id.run do
  let mut x : Array Nat := #[0,1,2,3]
  x[0] := 42
  x[1]   := 13
  x   --- prints: #[42, 13, 2, 3]

James Gallicchio (Oct 29 2022 at 22:52):

Was this ever committed somewhere? Seems a good addition to Std, particularly if we extend it to x[i][j] esque stuff

Gabriel Ebner (Oct 30 2022 at 00:03):

See also the later discussion at https://github.com/leanprover/lean4/issues/905

James Gallicchio (Oct 30 2022 at 00:28):

I'm not gonna lie, most of those syntax examples are pretty meaningless to me at first glance :joy: imperative programming is hard...

James Gallicchio (Oct 30 2022 at 00:28):

I'll stick to the functional stuff for now.


Last updated: Dec 20 2023 at 11:08 UTC