Zulip Chat Archive

Stream: new members

Topic: Using custom `Repr` of inductive type


Richard Kiss (Jun 27 2024 at 01:08):

I define Node with a custom Repr to reduce output size during proofs.

inductive Node
  | atom : Atom  Node
  | pair : Node  Node  Node


def repr_node (n: Node) : String :=
  match n with
  | Node.atom a => b2h a.data
  | Node.pair a b => s!"({repr_node a} . {repr_node b})"


instance : Repr Node where
  reprPrec (n : Node) (_ : Nat) := repr_node n

When I do #eval n where with n: Node my custom Repr works just fine. But in proofs it uses the default verbose Node.pair .... What am I missing?

Michal Wallace (tangentstorm) (Jun 27 2024 at 06:23):

I don't know the answer, but what happens if you also implement ToString?

Richard Kiss (Jun 27 2024 at 06:34):

I gave it a shot, but it didn't help. Looks like #eval toString n for n: Node does the same as repr_node except it puts double quotes around the whole thing

Daniel Weber (Jun 27 2024 at 06:42):

Could you make a #mwe? Anyway, I believe what you need is a delaborator, see https://leanprover-community.github.io/lean4-metaprogramming-book/extra/03_pretty-printing.html .

Daniel Weber (Jun 27 2024 at 06:42):

@[app_unexpander Node.pair]
def unexpNodePair : Lean.PrettyPrinter.Unexpander
  | `($(_) $a $b) => `(($a . $b))
  | _ => throw ()

should work

Richard Kiss (Jun 27 2024 at 18:34):

here's a simplified #mwe

structure Atom := mk :: (data: Nat)

def repr_atom (a: Atom) : String := s!"*{a.data}*"
  -- put stars around the nat to make it clear it's working

instance : Repr Atom where
  reprPrec (n : Atom) (_ : Nat) := repr_atom n


def a1 := Atom.mk 1
def a2 := Atom.mk 2

#eval a1 -- this works fine

theorem different: a1  a2 := by
  unfold a1
  unfold a2
  -- put cursor on this line and look at `Tactic state` in the Infoview
  -- it doesn't match the `#eval a1` output
  sorry

Richard Kiss (Jun 27 2024 at 18:36):

I tried playing around a bit with @[app_unexpander Atom] in the #mwe, adding

@[app_unexpander Atom]
def unexpNodePair : Lean.PrettyPrinter.Unexpander
  | `($(_) $a) => `(($a))
  | _ => throw ()

but it didn't seem to have any effect.

Richard Kiss (Jun 27 2024 at 18:37):

I'll read up more on elaborator, delaborator and the pretty-printing link above.

Henrik Böving (Jun 27 2024 at 18:38):

Daniel Weber said:

Could you make a #mwe? Anyway, I believe what you need is a delaborator, see https://leanprover-community.github.io/lean4-metaprogramming-book/extra/03_pretty-printing.html .

This is the correct direciton to go into.

There are two distinct ways to look at a datatype. One is Repr which you can implement to turn a value of your type to a string at runtime of a program (which is what eval is simulating). The other is controlled by the delaborator which determines how Lean shows you values etc. in its interactive editing facilities such as the LSP. Both of these facilities are inherently disconnected.

For your concrete use case you will need to put the delaborator on the constructor of Atom, Atom.mk instead of the type Atom which can of course have a custom interactive representation as well:

@[app_unexpander Atom.mk]
def unexpNodePair : Lean.PrettyPrinter.Unexpander
  | `($(_) $a) => `(($a))
  | _ => throw ()

Richard Kiss (Jun 27 2024 at 18:40):

got it! That worked. So I think I'm unblocked, thank you

Richard Kiss (Jun 27 2024 at 18:41):

is there a place I can read more about @[...] attributes? It smells like python decorators to me, but I haven't seen a formal definition of what it means

Henrik Böving (Jun 27 2024 at 18:42):

They are called attributes and they are quite different from decorators. If you wish to know how they work internally you can read up on it in the meta programming book that is linked above. If you just wish to work with them the answer is approximately the following: Tagging a declaration with an attribute will put it into a sort of mini database (potentially with additional meta information, in the case of app_unexpander that meta information is Atom.mk) within a Lean meta programming extension which can then do a kind of job. For app_unexpander that extension is the (surprise) unexpander which is part of the delaborator.

Richard Kiss (Jun 27 2024 at 20:55):

on further reflection, this makes sense. During proofs, we have to revert to how Atom is defined in their original context rather than creating instances of Atom and Repring them.

Henrik Böving (Jun 27 2024 at 21:22):

Precisley, note that in proofs your terms may have variables in them that you got from e.g. a function argument. You cannot Repr these variables as they are opaque values, you are doing symbolic reasoning after all.


Last updated: May 02 2025 at 03:31 UTC