Zulip Chat Archive

Stream: lean4

Topic: Quoting proofs with `ToExpr`


𝚠𝚘𝚓𝚌𝚒𝚎𝚌𝚑 𝚗𝚊𝚠𝚛𝚘𝚌𝚔𝚒 (May 01 2025 at 20:27):

If I have n : Nat, then I can write toExpr n : Expr to quote it into a kernel term. Suppose I also have a proof h : n = 2. Can I somehow quote it to obtain pf : Q($(toExpr n) = 2)?

Aaron Liu (May 01 2025 at 20:28):

The only way I know of doing this is to hard-code the proof.

Aaron Liu (May 01 2025 at 20:29):

since proofs are erased in the compiler

𝚠𝚘𝚓𝚌𝚒𝚎𝚌𝚑 𝚗𝚊𝚠𝚛𝚘𝚌𝚔𝚒 (May 01 2025 at 20:29):

I see what you're saying, maybe my question doesn't even make sense; it would be asking that toExpr produce an Expr from a runtime value that's effectively Unit.

Aaron Liu (May 01 2025 at 20:31):

What's your application?

Aaron Liu (May 01 2025 at 20:31):

For h : n = 2 the proof that $(toExpr n) = 2 is just rfl

𝚠𝚘𝚓𝚌𝚒𝚎𝚌𝚑 𝚗𝚊𝚠𝚛𝚘𝚌𝚔𝚒 (May 01 2025 at 20:35):

The actual use case is more complicated. I have some type, Foo, and a Foo-checking function partial def check (a : Foo) : Except String { _u : Unit // GoodFoo a }. Then I run it at the meta-level (in an elaborator), and if it succeeds, I wish to produce a proof in Q(GoodFoo $(toExpr a)). (Foo is the syntax of a type theory and check is a typechecker, but I think that doesn't matter here.)

𝚠𝚘𝚓𝚌𝚒𝚎𝚌𝚑 𝚗𝚊𝚠𝚛𝚘𝚌𝚔𝚒 (May 01 2025 at 20:38):

(Thinking about it further, being able to reflect proofs like this is some analogue of nativeDecide, since the original checking runs in the interpreter.)

Aaron Liu (May 01 2025 at 20:40):

This is just decide

Aaron Liu (May 01 2025 at 20:40):

Write a Decidable instance

Aaron Liu (May 01 2025 at 20:41):

Actually, is this decide? It might be partial.

Aaron Liu (May 01 2025 at 20:41):

hmmmmmmmm

𝚠𝚘𝚓𝚌𝚒𝚎𝚌𝚑 𝚗𝚊𝚠𝚛𝚘𝚌𝚔𝚒 (May 01 2025 at 20:42):

Aaron Liu said:

Actually, is this decide? It might be partial.

Sorry yeah, it's partial def check.

Aaron Liu (May 01 2025 at 20:42):

oh no

Aaron Liu (May 01 2025 at 20:43):

so you can't prove anything at all?

Aaron Liu (May 01 2025 at 20:44):

I have an idea! You can make check return the Expr instead of the useless proof.

𝚠𝚘𝚓𝚌𝚒𝚎𝚌𝚑 𝚗𝚊𝚠𝚛𝚘𝚌𝚔𝚒 (May 01 2025 at 20:45):

Aaron Liu said:

I have an idea! You can make check return the Expr instead of the useless proof.

Indeed this would be the standard approach, I just really didn't want to do that (because it makes the code of check strictly less readable); but I may have to.

𝚠𝚘𝚓𝚌𝚒𝚎𝚌𝚑 𝚗𝚊𝚠𝚛𝚘𝚌𝚔𝚒 (May 01 2025 at 21:16):

So what such a reflection principle is really asserting is that all the ToExpr implementations are correct, at least on GoodFoos. In certain cases we can prove this directly - or more accurately, construct Q(GoodFoo $(toExpr a)) - without changing check itself. I think this can only work if the proof term is constructible by recursion on a only (so on data, not on a proof), which may need some inversion principles.

inductive GoodNat : Nat  Prop where
  | one : GoodNat 1
  | succ {n} : GoodNat n  GoodNat (n + 1)

theorem GoodNat.pred {n} : GoodNat (n + 2)  GoodNat (n + 1)
  | succ h => h

def GoodNat.toExpr {n} (h : GoodNat n) : let n : Q(Nat) := toExpr n; Q(GoodNat $n) :=
  match n with
  | 1 => q(.one)
  | _+2 =>
    let e := GoodNat.pred h |>.toExpr
    q(.succ $e)

Kyle Miller (May 01 2025 at 21:40):

(Something that's probably not useful for you, but I was reminded of this recently: sometimes having a runtime value is enough to synthesize a proof, though probably most cases are fairly trivial. The only one that comes to mind is docs#instToExprFin)


Last updated: May 02 2025 at 03:31 UTC