Zulip Chat Archive

Stream: lean4

Topic: anonymous structure


Tomas Skrivan (Nov 04 2025 at 20:57):

For quite some time I wanted to have anonymous structures.

My main use case is when a function returns multiple values. Often I end up defining one time use structure likeFooResult for function foo. However more often I just end up returning a type that looks like _ × _ × _ × _ × _ which I often regret when I read the code after some time.

So I finally took the time and defined anonymous structures. You can write code like

def foo (a : Float) : struct {x : Float, y : Float} :=
  { x := a.cos, y := a.sin }

instance : Add (struct { x : Float, y : Float }) := fun u v => u.x+v.x, u.y+v.y⟩⟩

/-- info: { x := 1.916065, y := 0.489252 } -/
#guard_msgs in
#eval
  let a := foo 0.1
  let b := foo 0.4
  a + b

No question here, I just want to share this as I'm sure someone else will find it useful too.

Here is the implementation

code

Kenny Lau (Nov 04 2025 at 20:59):

very nice! just one tiny detail, I would write struct% to make it clear that this is a meta function

Tomas Skrivan (Nov 04 2025 at 21:00):

Ohh one question regardless. Can someone help me to fix the unexpander? It uses syntax quotation inside of quotation and all the identifiers get murdered with

/-- info: struct {x✝:Float✝, y✝:Float✝} : Type -/
#check struct { x : Float, y : Float }

Kenny Lau (Nov 04 2025 at 21:03):

instead of `(struct { $[$names:ident : $types:term],* }) I would do the parsing separately, maybe this would fix that issue

Tomas Skrivan (Nov 04 2025 at 21:07):

Kenny Lau said:

very nice! just one tiny detail, I would write struct% to make it clear that this is a meta function

Yeah that would work too but I feel that the % notation is used for elaborators that do substantially more complicated things. I feel that anonymous struct is quite a simple concept.

Tomas Skrivan (Nov 05 2025 at 06:36):

Kenny Lau said:

instead of `(struct { $[$names:ident : $types:term],* }) I would do the parsing separately, maybe this would fix that issue

What do you mean by passing separately?

I might be wrong but I thought the issue is in

let cmd  `(command|
    @[app_unexpander $structId]
    def unexpandAnonStruct : Lean.PrettyPrinter.Unexpander
    | _ => `($stx)
    )

which contains nested quotation `

František Silváši 🦉 (Nov 05 2025 at 10:21):

I wonder if this isn't a legitimate use of suspending hygiene:

@[app_unexpander $structId]
    def unexpandAnonStruct : Lean.PrettyPrinter.Unexpander
    | _ => set_option hygiene false in `($stx)
    )

Considering the macro scopes have already been 'handled' by this point (i.e. stx is the thing you want verbatim here)?

Tomas Skrivan (Nov 05 2025 at 10:57):

Thanks a lot! works like a charm!

Yes it feels like a legitimate use case but I don't really understand syntax and macro scopes so hard to say.


Last updated: Dec 20 2025 at 21:32 UTC