Zulip Chat Archive
Stream: lean4
Topic: What's needed for mutual defs?
Arthur Paulino (Jun 15 2022 at 13:49):
Hi! Not sure how troublesome this is but we're trying to make mutual
blocks with a somewhat minimal baggage. What else do we need to make this work?
prelude
def id (A : Type) (x : A) : A := x
inductive Unit where
| unit
inductive Bar
| hi | bye
def bar := Bar.hi
inductive MyNat
| zero : MyNat
| succ : MyNat → MyNat
structure PProd (α : Sort u) (β : Sort v) where
fst : α
snd : β
mutual
def f : MyNat → MyNat
| .zero => .zero
-- tactic 'cases' failed, nested error:
-- tactic 'cases' failed, not applicable to the given hypothesis
-- _fun_discr✝ : MyNat
-- motive : MyNat → Sort ?u.74
-- h_1 : Unit → motive MyNat.zero
-- h_2 : (n : MyNat) → motive (MyNat.succ n)
-- ⊢ motive _fun_discr✝ after processing
-- _
-- the dependent pattern matcher can solve the following kinds of equations
-- - <var> = <term> and <term> = <var>
-- - <term> = <term> where the terms are definitionally equal
-- - <constructor> = <constructor>, examples: List.cons x xs = List.cons y ys, and List.cons x xs = List.nil
-- | .succ n => (g n).succ
def g : MyNat → MyNat
| .zero => .zero
| .succ n => (f n).succ
end
Sebastian Ullrich (Jun 15 2022 at 13:54):
You should probably get comfortable with navigating the Lean source code if that's your goal. For example, starting at "tactic 'cases' failed, not applicable to the given hypothesis" you should soon find the next missing type.
Arthur Paulino (Jun 15 2022 at 14:05):
Sorry, the error message is cryptic to me :/
I can't parse that there is a missing type, so I can't parse what the missing type is
Leonardo de Moura (Jun 15 2022 at 14:05):
BTW, when defining your own prelude, be aware of the issue https://github.com/leanprover/lean4/issues/188
Sebastian Ullrich (Jun 15 2022 at 14:10):
Arthur Paulino said:
Sorry, the error message is cryptic to me :/
I can't parse that there is a missing type, so I can't parse what the missing type is
That's why I talked about navigating the codebase: if you grep for the message (preferable the second half, as the first half could be templated), you're already very close.
Mario Carneiro (Jun 15 2022 at 14:19):
@Leonardo de Moura Regarding lean4#188, I think lean should have something like rust's "lang items": when you declare Nat
you would put a @[lang]
(or @[lang = "Nat"]
) attribute on it and this would use the given key / the declaration name to determine what to check about the definition
Mario Carneiro (Jun 15 2022 at 14:20):
This is also useful for documenting which types have extra special compiler magic on top of the usual
Mario Carneiro (Jun 15 2022 at 14:29):
(The @[lang = "Nat"]
form would allow decoupling lang items from their names, so that for example you could declare it to have the name nat
instead in a custom prelude. But this would require plumbing the name lookups everywhere instead of hardcoding names like `Nat.add
around and personally I don't think it's worth it.)
Arthur Paulino (Jun 15 2022 at 14:31):
Sebastian Ullrich said:
grep for the message
Ah, now I understood the idea. Thanks!
Arthur Paulino (Jun 15 2022 at 19:07):
So, apparently this match is falling in the "anything else" case, which means that casesOn
is not being found in the environment. However, I couldn't find a casesOn
type that I could add to my prelude file :sad:
Arthur Paulino (Jun 15 2022 at 19:34):
I tried another approach: copy increasingly longer bits of Prelude.lean
until it works.
Apparently no more types are missing, but I don't have what's needed to allow Lean to prove terminations by itself. How does that work?
Arthur Paulino (Jun 15 2022 at 19:34):
This is the #mwe:
prelude
universe u v w
@[inline] def id {α : Sort u} (a : α) : α := a
@[inline] def Function.comp {α : Sort u} {β : Sort v} {δ : Sort w} (f : β → δ) (g : α → β) : α → δ :=
fun x => f (g x)
@[inline] def Function.const {α : Sort u} (β : Sort v) (a : α) : β → α :=
fun x => a
set_option checkBinderAnnotations false in
@[reducible] def inferInstance {α : Sort u} [i : α] : α := i
set_option checkBinderAnnotations false in
@[reducible] def inferInstanceAs (α : Sort u) [i : α] : α := i
set_option bootstrap.inductiveCheckResultingUniverse false in
inductive PUnit : Sort u where
| unit : PUnit
/-- An abbreviation for `PUnit.{0}`, its most common instantiation.
This Type should be preferred over `PUnit` where possible to avoid
unnecessary universe parameters. -/
abbrev Unit : Type := PUnit
@[matchPattern] abbrev Unit.unit : Unit := PUnit.unit
/-- Auxiliary unsafe constant used by the Compiler when erasing proofs from code. -/
unsafe axiom lcProof {α : Prop} : α
/-- Auxiliary unsafe constant used by the Compiler to mark unreachable code. -/
unsafe axiom lcUnreachable {α : Sort u} : α
inductive True : Prop where
| intro : True
inductive False : Prop
inductive Empty : Type
set_option bootstrap.inductiveCheckResultingUniverse false in
inductive PEmpty : Sort u where
def Not (a : Prop) : Prop := a → False
@[macroInline] def False.elim {C : Sort u} (h : False) : C :=
False.rec (fun _ => C) h
@[macroInline] def absurd {a : Prop} {b : Sort v} (h₁ : a) (h₂ : Not a) : b :=
False.elim (h₂ h₁)
inductive Eq : α → α → Prop where
| refl (a : α) : Eq a a
@[simp] abbrev Eq.ndrec.{u1, u2} {α : Sort u2} {a : α} {motive : α → Sort u1} (m : motive a) {b : α} (h : Eq a b) : motive b :=
Eq.rec (motive := fun α _ => motive α) m h
@[matchPattern] def rfl {α : Sort u} {a : α} : Eq a a := Eq.refl a
@[simp] theorem id_eq (a : α) : Eq (id a) a := rfl
theorem Eq.subst {α : Sort u} {motive : α → Prop} {a b : α} (h₁ : Eq a b) (h₂ : motive a) : motive b :=
Eq.ndrec h₂ h₁
theorem Eq.symm {α : Sort u} {a b : α} (h : Eq a b) : Eq b a :=
h ▸ rfl
theorem Eq.trans {α : Sort u} {a b c : α} (h₁ : Eq a b) (h₂ : Eq b c) : Eq a c :=
h₂ ▸ h₁
@[macroInline] def cast {α β : Sort u} (h : Eq α β) (a : α) : β :=
Eq.rec (motive := fun α _ => α) a h
theorem congrArg {α : Sort u} {β : Sort v} {a₁ a₂ : α} (f : α → β) (h : Eq a₁ a₂) : Eq (f a₁) (f a₂) :=
h ▸ rfl
theorem congr {α : Sort u} {β : Sort v} {f₁ f₂ : α → β} {a₁ a₂ : α} (h₁ : Eq f₁ f₂) (h₂ : Eq a₁ a₂) : Eq (f₁ a₁) (f₂ a₂) :=
h₁ ▸ h₂ ▸ rfl
theorem congrFun {α : Sort u} {β : α → Sort v} {f g : (x : α) → β x} (h : Eq f g) (a : α) : Eq (f a) (g a) :=
h ▸ rfl
init_quot
inductive HEq : {α : Sort u} → α → {β : Sort u} → β → Prop where
| refl (a : α) : HEq a a
@[matchPattern] protected def HEq.rfl {α : Sort u} {a : α} : HEq a a :=
HEq.refl a
theorem eq_of_heq {α : Sort u} {a a' : α} (h : HEq a a') : Eq a a' :=
have : (α β : Sort u) → (a : α) → (b : β) → HEq a b → (h : Eq α β) → Eq (cast h a) b :=
fun α β a b h₁ =>
HEq.rec (motive := fun {β} (b : β) (h : HEq a b) => (h₂ : Eq α β) → Eq (cast h₂ a) b)
(fun (h₂ : Eq α α) => rfl)
h₁
this α α a a' h rfl
structure Prod (α : Type u) (β : Type v) where
fst : α
snd : β
attribute [unbox] Prod
/-- Similar to `Prod`, but `α` and `β` can be propositions.
We use this Type internally to automatically generate the brecOn recursor. -/
structure PProd (α : Sort u) (β : Sort v) where
fst : α
snd : β
structure And (a b : Prop) : Prop where
intro :: (left : a) (right : b)
inductive PSum (α : Sort u) (β : Sort v) where
| inl (val : α) : PSum α β
| inr (val : β) : PSum α β
inductive Bar
| hi | bye
def bar := Bar.hi
inductive MyNat
| zero : MyNat
| succ : MyNat → MyNat
mutual
def f : MyNat → MyNat
| .zero => .zero
| .succ n => (g n).succ
def g : MyNat → MyNat
| .zero => .zero
| .succ n => (f n).succ
end
Henrik Böving (Jun 15 2022 at 19:38):
When I check your example with ordinary nats:
mutual
def f : Nat → Nat
| .zero => .zero
| .succ n => (g n).succ
def g : Nat → Nat
| .zero => .zero
| .succ n => (f n).succ
end
And print what they elaborate to:
def f : Nat → Nat :=
fun x => f._mutual (PSum.inl x)
There is this magical _mutual declaration which if you print it is quite huge and among other things requires WellFounded recursion and the corresponding instances for well founded relation etc.
So I would imagine you need at least parts of Init.WF
and maybe also Init.WFTactics
Henrik Böving (Jun 15 2022 at 19:40):
(Which as you can see in doc-gen adds up to a whole lot of files because these already make use of quite a few imports)
Arthur Paulino (Jun 15 2022 at 19:42):
Alright, thanks!!
I will try another route then :sweat_smile:
Henrik Böving (Jun 15 2022 at 19:45):
I don't really think you can avoid well founded recursion here though, mutual just always seems to involve using well founded recursion to show termination so you won't be able to get around it. How much of it you need for your trivial examples is a completely different question though
Arthur Paulino (Jun 15 2022 at 19:49):
Yeah turns out my plan to end up with a very small environment was slipping through my fingers
Henrik Böving (Jun 15 2022 at 19:50):
Why exactly are you doing this btw?
Arthur Paulino (Jun 15 2022 at 19:52):
Long story short, we're trying to achieve some kind of "translation" of the Lean environment through metaprogramming. And I'm trying to test our method to translate mutual definitions
Henrik Böving (Jun 15 2022 at 19:54):
But if you were to just translate every declaration that is in a regular Environment
right now things would just work out by themselves no? After all in a regular one all of this stuff is in place.
Arthur Paulino (Jun 15 2022 at 19:55):
Yeah but the environment is HUGE, so I was looking for a small and controlled environment
Henrik Böving (Jun 15 2022 at 19:58):
It definitley is yes, but e.g. before I started to try and obtain all of the equational lemmata for doc-gen (which requires running additional meta code with tactics for every def
because they are calculated dynamically) doc-gen did actually run through an environment with a fully loaded mathlib quite quickly, pretty printing and analyzing every declaration and even now it is in the minute span at most so processing large environments in short time definitley is possible.
Henrik Böving (Jun 15 2022 at 19:59):
ah it is not a timing concern i see.
Henrik Böving (Jun 15 2022 at 20:02):
In that case maybe you could use the regular environment and write some code that recursively figures out what declarations a certain other declaration depends on so you can make a list of things for your mutual
example and only translate all the declarations that are required for it?
Arthur Paulino (Jun 15 2022 at 20:06):
Yeah that's precisely what I'm trying right now
John Burnham (Jun 15 2022 at 21:43):
Arthur Paulino said:
Long story short, we're trying to achieve some kind of "translation" of the Lean environment through metaprogramming. And I'm trying to test our method to translate mutual definitions
A slightly longer version of the story is that we're trying to transform a Lean.Environment
which is a map keyed on names, to a map keyed on cryptographic hashes of a name-irrelevant representation of the constants (vaguely similar to the hashing of https://www.unison-lang.org/). This lets us content-address any constant in a way that bypasses the hierarchical namespace. So
def id1 (A : Type) (x: A) : A := x
def id2 (B : Type) (y: B) : B := y
which are identical except for names, would hash to the same anonymous constant.
Anyway, to do this for mutual
blocks is complicated (since you can't hash a cyclic reference graph in the same way you would an acyclic one), so we're trying to create some simple test cases to try our solution against
Henrik Böving (Jun 15 2022 at 22:29):
A this is a thing I've been wondering for a while: Why exactly is this desirable? :D
Henrik Böving (Jun 15 2022 at 22:30):
I get that it has a certain "technical beauty" appeal but i never understood why people actually want this.
John Burnham (Jun 15 2022 at 23:03):
Well, there are a lot of potential benefits to content addressing (like for build systems etc). But the concrete use-case our Yatima project (https://github.com/yatima-inc/yatima-lang/wiki) needs this for is that we're compiling Lean to the Lurk language for recursive zkSNARKs (https://github.com/lurk-lang), so that we can have lightweight cryptographic certificates that myProof : myTheorem
validly checks (which can be verified in constant time without re-running a kernel)
Having nameless content-addressing allows us to compose any such certificate of proof1 : Theorem1
with any other certificate ofproof2 : Theorem2
irrespective of their use of the hierarchical namespace (like if they rely on two different constants both with the same name). This greatly improves certificate generation performance
John Burnham (Jun 15 2022 at 23:08):
I know there have been threads around Unison-style content-addressing for Lean (or lake
) here in the past, and totally open to generalizing Yatima's content-addressing schema if that's of interest. But we're implementing this for a very domain-specific reason, which may have different constraints.
One thing though is that our content-addressing uses https://ipld.io/, which will let you composably share Lean constants and environments over IPFS (https://ipfs.io/), which is kinda neat
Mac (Jun 16 2022 at 07:36):
Henrik Böving said:
A this is a thing I've been wondering for a while: Why exactly is this desirable? :D
Outside of a concrete use cases like John mentioned there are two other notable general advantages: it eliminates code bloat caused by cross-package code duplication and it allows for easy, transparent aliasing of definitions (ie.g., nat
, Nat
, natural
, Natural
can all resolve to the same thing). The later feature is general to any language that decouples names from unit identifiers .
Last updated: Dec 20 2023 at 11:08 UTC