Zulip Chat Archive

Stream: lean4

Topic: controlling delaboration


David Renshaw (May 24 2021 at 23:11):

I can define custom syntax via infix like this, and it gets printed back nicely when I do #check:

infix " ♫ "  => LE.le
#check  (1  2)
-- 1 ♫ 2 : Prop

If I instead define the syntax like this, then it does not get printed back nicely:

syntax term " ♫♫ " term : term
macro_rules
|`( $a ♫♫ $b ) => `( LT.lt $a $b )

#check 1 ♫♫ 2
-- 1 < 2 : Prop

What could I add to the second example to make it behave like the first?

Daniel Selsam (May 24 2021 at 23:48):

syntax term " ♫♫ " term : term
macro_rules
|`( $a ♫♫ $b ) => `( LT.lt $a $b )

#check 1 ♫♫ 2 -- 1 < 2 : Prop

@[appUnexpander LT.lt] def unexpandMusic : Lean.PrettyPrinter.Unexpander
  | `(LT.lt $a $b) => `($a ♫♫ $b)
  | _              => throw ()

#check 1 ♫♫ 2 -- 1 ♫♫ 2 : Prop

David Renshaw (May 27 2021 at 23:15):

Is it possible to do something like that for a structure? It's not working for me:

structure Foo where
  x : Nat
  y : Nat

syntax term " ♬ " term : term

macro_rules
| `( $a  $b ) => `(Foo.mk $a $b)

#check 1  2 -- { x := 1, y := 2 }

@[appUnexpander Foo] def unexpandFoo : Lean.PrettyPrinter.Unexpander
  | `({ x:= $a, y:= $b }) => `($a  $b)
  | _ => throw ()

#check 3  4 -- { x := 3, y := 4 }

Mario Carneiro (May 27 2021 at 23:44):

I managed to make it work with a delaborator:

open Lean PrettyPrinter.Delaborator
@[delab app.Foo.mk] def delabFoo : Delab := do
  let e  getExpr
  guard $ e.getAppNumArgs == 2
  let a  withAppFn $ withAppArg delab
  let b  withAppArg delab
  `($a  $b)

#check 3  4 -- 3 ♬ 4 : Foo

Daniel Selsam (May 28 2021 at 00:23):

Currently, appUnexpanding does not work if a more specialized delaborator applies (in this case, the structure delaborator).

Daniel Selsam (May 28 2021 at 00:31):

I think users may expect custom unexpanders to take priority over builtin delaborators, so it may be worth addressing. It seems like an easy fix: just fail in these two cases (https://github.com/leanprover/lean4/blob/master/src/Lean/PrettyPrinter/Delaborator/Builtins.lean#L163-L165) and put it at the end of the list.

Daniel Selsam (May 28 2021 at 00:34):

It might also make sense to use the first unexpander that works rather than giving up after the first.

Daniel Selsam (May 28 2021 at 02:32):

Actually, this proposal would introduce a major perf problem unless it caches the delabAppImplicit calls. Probably not worth it.

Daniel Selsam (May 28 2021 at 02:37):

Not so bad actually https://github.com/dselsam/lean4/tree/unexpand

Sebastian Ullrich (May 28 2021 at 07:15):

@Daniel Selsam Can't we just move the delabAppImplicit call into the some expanders branch? Then I would not be worried about overhead.

Sebastian Ullrich (May 28 2021 at 07:19):

Assuming that most unexpanders succeed, given that most function applications are saturated

David Renshaw (May 28 2021 at 23:25):

How do I go from a value (numLit "3") : Syntax to a value 3 : Nat?

David Renshaw (May 28 2021 at 23:25):

E.g.

structure Foo where
  x : Nat
  y : Nat

syntax term " ♬ " term : term

macro_rules
| `( $a  $b ) => `(Foo.mk $a $b)

open Lean PrettyPrinter.Delaborator
@[delab app.Foo.mk] def delabFoo : Delab := do
  let e  getExpr
  guard $ e.getAppNumArgs == 2
  let a  withAppFn $ withAppArg delab
  let b  withAppArg delab
  let x : Nat := a -- Doesn't work. Probably need to invoke some kind of parser?
  -- do something with x ...
  `($a  $b)

Mario Carneiro (May 28 2021 at 23:28):

Is it a guaranteed literal, or can it be a closed expression that requires evaluation?

David Renshaw (May 28 2021 at 23:29):

For now I think I only care about the guaranteed literal case.

Mario Carneiro (May 28 2021 at 23:31):

easiest way seems to be a.isNatLit?.get!

David Renshaw (May 28 2021 at 23:34):

How did you look that up? Using code completion in VSCode? (I'm using emacs.)

Mario Carneiro (May 28 2021 at 23:34):

No, grepping the source for useful looking things

Mario Carneiro (May 28 2021 at 23:35):

specifically I found numLitKind and looked for references to it

Mario Carneiro (May 28 2021 at 23:37):

code completion does work but the list of things you can do to a Syntax is pretty large

David Renshaw (May 29 2021 at 03:57):

Aha -- apparently Lean.Meta.whnf lets me handle cases where a is not guaranteed to be a literal

Daniel Selsam (Jun 08 2021 at 17:27):

Sebastian Ullrich said:

Daniel Selsam Can't we just move the delabAppImplicit call into the some expanders branch? Then I would not be worried about overhead.

That might be an acceptable trade-off, but the following would take exponential time to delaborate:

def foo (k : Nat  Nat) (n : Nat) : Nat := k (n+1)

set_option hygiene false in
@[appUnexpander foo] def unexpandFooApp : Lean.PrettyPrinter.Unexpander
  | `(foo $k $a) => `(My.foo $k $a)
  | _ => throw ()

#check foo id 0 -- My.foo id
#check foo $ foo $ foo $ foo $ foo $ foo $ foo $ foo $ foo $ foo $ foo $ foo $ foo $ foo $ foo $ foo id -- exp-time

Last updated: Dec 20 2023 at 11:08 UTC