Zulip Chat Archive
Stream: lean4
Topic: is there a way to rename a type such as N := Nat
Xiaoning Bian (Jan 14 2024 at 23:24):
is there a way to rename a type such as N := Nat
Alok Singh (Jan 14 2024 at 23:38):
abbrev? abbrev N := Nat
Alex J. Best (Jan 14 2024 at 23:39):
Or you can use notation like ℕ
as defined in libraries
Xiaoning Bian (Jan 14 2024 at 23:45):
thanks. 'abbrev' works! I used 'def' before, Lean4 cannot recognize elements of N. Strangely, I roughly went though some doc, but I didn't see any about 'abbrev'. Everyone is using Nat instead of N.
Xiaoning Bian (Jan 14 2024 at 23:46):
Alex J. Best said:
Or you can use notation like
ℕ
as defined in libraries
Yes, I mean ℕ. Just don't know how to type it in chat. Thanks.
Alex J. Best (Jan 14 2024 at 23:52):
My point is that in Mathlib we have a line
notation "ℕ" => Nat
which is probably closer to what you want than abbrev then
Xiaoning Bian (Jan 14 2024 at 23:57):
Alex J. Best said:
My point is that in Mathlib we have a line
notation "ℕ" => Nat
which is probably closer to what you want than abbrev then
I see. Thanks. I just tried 'notation'. It works.
Xiaoning Bian (Jan 15 2024 at 00:30):
BTW, in Agda, 'N = Nat' works. '=' in Lean4 is used for equality. '=' in Agda is for any kinds of renaming: N = Nat, f = g, a = 1. the following code works in Agda.
a = 1
M = ℕ
f : M -> M
f zero = a
f (suc x) = x
g = f
So, syntax in Lean4 is a little bit convoluted.
Mario Carneiro (Jan 15 2024 at 03:02):
The equivalent of N = Nat
in lean is def N := Nat
Mario Carneiro (Jan 15 2024 at 03:02):
it's just not necessarily what you want to do in this instance
Xiaoning Bian (Jan 15 2024 at 03:14):
Mario Carneiro said:
The equivalent of
N = Nat
in lean isdef N := Nat
For some reason, 'def N := Nat' won't work. I tried that. E.g.
-- These two worked.
-- notation "ℕ" => Nat
--abbrev ℕ := Nat
-- But not this one:
def ℕ := Nat
inductive Ket : ℕ -> Type where
| Ket1 : Bool -> (Ket 1)
| KetS : {n : Nat} -> Ket 1 -> Ket n -> Ket (n + 1)
Mario Carneiro (Jan 15 2024 at 03:21):
that's because in lean def
initions are opaque to typeclass inference (which is a feature, it means you can put different typeclasses on a newtype)
Mario Carneiro (Jan 15 2024 at 03:22):
I think there is no such distinction in agda
Xiaoning Bian (Jan 15 2024 at 04:34):
Mario Carneiro said:
that's because in lean
def
initions are opaque to typeclass inference (which is a feature, it means you can put different typeclasses on a newtype)
1) Agda does not support typeclass (but you can code your own in Agda). It seems that once you use '=' in Agda, then lhs is rhs without any difference(I like it).
2) We can alway wrap a type in a new type if we want this feature in Agda:
data NN : Set where
Wrap : ℕ -> NN
This seems also the only way to make new typeclass instances in Haskell when a type already has such an instance. E.g. I might want a different Show instance for Nat, then I have to wrap Nat in a new type. We can do the same in Lean4.
3)
instance OfNat ℕ n where
ofNat := n
worked, but it's very confusing, since the OfNat definition
class OfNat (α : Type) (_ : Nat) where
ofNat : α
indicates n : Nat, but ofNat := n is of type ℕ . Why sometimes Lean4 reacognizes ℕ is Nat, and sometimes doesn't. Also,
instance OfNat ℕ 0 where
ofNat := 0
won't typecheck. It worked for all n, but why not for an instance 0?
4) I just checked, using 'def N := Nat' won't let you do pattern matching on N. So why are we using and allowing 'def N := Nat'? what's the purpose of 'def N := Nat'? what's the constructor of N?
Trebor Huang (Jan 15 2024 at 04:36):
Agda supports typeclasses, but during typeclass resolution, the same level of judgemental equality is used as implicit variable resolution.
Mario Carneiro (Jan 15 2024 at 04:37):
1) Agda does not support typeclass (but you can code your own in Agda). It seems that once you use '=' in Agda, then lhs is rhs without any difference(I like it).
What do you mean by "without any difference"? There are clearly some differences, like the spelling. In lean there are quite a few notions of equality at different levels of strictness and notation
, abbrev
, def
and irreducible_def
produce things that are "the same" as their bodies at these different levels of strictness
Mario Carneiro (Jan 15 2024 at 04:38):
notation
produces the strictest level of equality, the LHS is literally expanded to the RHS at parse time. But you certainly don't want to write a whole library development that way, terms would get exponentially large
Kyle Miller (Jan 15 2024 at 04:47):
@Xiaoning Bian Re 4, yes you can do pattern matching.
def N := Nat
def add : N → N → N
| m, .zero => m
| m, .succ n => .succ (add m n)
Kyle Miller (Jan 15 2024 at 04:48):
Re 2, Haskell's newtype
is like Lean's def
(though not completely, since Lean's can still unfold), and Haskell's type
is like Lean's abbrev
. You can also use data
in Haskell, or data
in Agda, or structure
/inductive
in Lean to create a completely new type by wrapping.
Mario Carneiro (Jan 15 2024 at 04:52):
Here's a demonstration of 5 different kinds of equality in lean and the things that differentiate them:
import Mathlib.Tactic.IrreducibleDef
import Mathlib.Logic.Equiv.Basic
notation "T1" => Nat
abbrev T2 := Nat
def T3 := Nat
irreducible_def T4 := Nat
structure T5 := val : Nat
-- syntactic equality means that there is no difference between x0 and x1
def x0 := Nat #print x0 -- def x0 : Type := T1
def x1 := T1 #print x1 -- def x1 : Type := T1
def x2 := T2 #print x2 -- def x2 : Type := T2
def x3 := T3 #print x3 -- def x3 : Type := T3
def x4 := T4 #print x4 -- def x4 : Type := T4
def x5 := T5 #print x5 -- def x5 : Type := T5
-- reducible equality means that tactics like simp will not differentiate them
example {α} (h : Nat = α) : T1 = α := by simp only [h] -- ok
example {α} (h : Nat = α) : T2 = α := by simp only [h] -- ok
example {α} (h : Nat = α) : T3 = α := by simp only [h] -- failed
example {α} (h : Nat = α) : T4 = α := by simp only [h] -- failed
example {α} (h : Nat = α) : T5 = α := by simp only [h] -- failed
-- definitional equality means that rfl proves the equality
example : T1 = Nat := rfl -- ok
example : T2 = Nat := rfl -- ok
example : T3 = Nat := rfl -- ok
example : T4 = Nat := rfl -- failed
example : T5 = Nat := rfl -- failed
-- propositional equality means that the equality is provable
example : T1 = Nat := rfl -- ok
example : T2 = Nat := rfl -- ok
example : T3 = Nat := rfl -- ok
example : T4 = Nat := T4_def -- ok
example : T5 = Nat := sorry -- not provable
-- equivalence means that the types have the same elements
example : T1 ≃ Nat := .refl _
example : T2 ≃ Nat := .refl _
example : T3 ≃ Nat := .refl _
example : T4 ≃ Nat := T4_def ▸ .refl _
example : T5 ≃ Nat := ⟨T5.val, T5.mk, fun _ => rfl, fun _ => rfl⟩
Xiaoning Bian (Jan 15 2024 at 04:58):
Trebor Huang said:
Agda supports typeclasses, but during typeclass resolution, the same level of judgemental equality is used as implicit variable resolution.
Thanks for pointing it out. Yeah, you can use "instance argument" to mimic typeclass. I mentioned you can code your own meaning using instance argument. Agda just doesn't have a 'typeclass' keyword as in Haskell, or 'class' as in Lean4. But you have to use the special syntax to open a record in Agda.
Mario Carneiro (Jan 15 2024 at 05:01):
Xiaoning Bian said:
3)
instance OfNat ℕ n where
ofNat := nworked, but it's very confusing, since the OfNat definition
class OfNat (α : Type) (_ : Nat) where
ofNat : αindicates n : Nat, but ofNat := n is of type ℕ .
This works because the types ℕ
and Nat
are definitionally equal (the rfl
test just above). Another place where definitional equality comes up is that if x : A
and A
and B
are definitionally equal then lean accepts x : B
.
Why sometimes Lean4 reacognizes ℕ is Nat, and sometimes doesn't. Also,
instance OfNat ℕ 0 where
ofNat := 0won't typecheck. It worked for all n, but why not for an instance 0?
This is because 0
is type-generic, it uses the OfNat
instance to find it and since you are in the middle of defining that instance it of course can't find one yet. You could write show Nat from 0
though to force lean to look for 0
as a Nat
, and then assert that it has type ℕ
, instead of looking for 0 : ℕ
directly which will require the OfNat ℕ 0
instance.
Mario Carneiro (Jan 15 2024 at 05:02):
4) I just checked, using 'def N := Nat' won't let you do pattern matching on N. So why are we using and allowing 'def N := Nat'? what's the purpose of 'def N := Nat'? what's the constructor of N?
You can do pattern matching on N
, you just can't use 0
and +1
because these are generic operators and will cause instance search for OfNat
and HAdd
respectively, which will fail on the new type (unless you define these, of course). If you use Nat.zero
and Nat.succ
it will work
Xiaoning Bian (Jan 15 2024 at 05:16):
Kyle Miller said:
Re 2, Haskell's
newtype
is like Lean'sdef
(though not completely, since Lean's can still unfold), and Haskell'stype
is like Lean'sabbrev
. You can also usedata
in Haskell, ordata
in Agda, orstructure
/inductive
in Lean to create a completely new type by wrapping.
Thanks. I didn't know I need put a dot before a constuctor. I come from Agda, there, a dot pattern means a different thing. I am very happy to know "type ~ abbrev". "data = newtype ~ ~ def" is also good to know. Thanks.
Kyle Miller (Jan 15 2024 at 05:21):
This use of dot means "use the expected type to figure out the namespace for the function". It's able to unfold def
s while figuring this out.
Here's an alternative:
def N := Nat
open Nat
def add : N → N → N
| m, zero => m
| m, succ n => succ (add m n)
Or you can write Nat.zero
and Nat.succ
instead of using open Nat
.
Xiaoning Bian (Jan 15 2024 at 05:40):
Mario Carneiro said:
Here's a demonstration of 5 different kinds of equality in lean and the things that differentiate them:
import Mathlib.Tactic.IrreducibleDef import Mathlib.Logic.Equiv.Basic notation "T1" => Nat abbrev T2 := Nat def T3 := Nat irreducible_def T4 := Nat structure T5 := val : Nat -- syntactic equality means that there is no difference between x0 and x1 def x0 := Nat #print x0 -- def x0 : Type := T1 def x1 := T1 #print x1 -- def x1 : Type := T1 def x2 := T2 #print x2 -- def x2 : Type := T2 def x3 := T3 #print x3 -- def x3 : Type := T3 def x4 := T4 #print x4 -- def x4 : Type := T4 def x5 := T5 #print x5 -- def x5 : Type := T5 -- reducible equality means that tactics like simp will not differentiate them example {α} (h : Nat = α) : T1 = α := by simp only [h] -- ok example {α} (h : Nat = α) : T2 = α := by simp only [h] -- ok example {α} (h : Nat = α) : T3 = α := by simp only [h] -- failed example {α} (h : Nat = α) : T4 = α := by simp only [h] -- failed example {α} (h : Nat = α) : T5 = α := by simp only [h] -- failed -- definitional equality means that rfl proves the equality example : T1 = Nat := rfl -- ok example : T2 = Nat := rfl -- ok example : T3 = Nat := rfl -- ok example : T4 = Nat := rfl -- failed example : T5 = Nat := rfl -- failed -- propositional equality means that the equality is provable example : T1 = Nat := rfl -- ok example : T2 = Nat := rfl -- ok example : T3 = Nat := rfl -- ok example : T4 = Nat := T4_def -- ok example : T5 = Nat := sorry -- not provable -- equivalence means that the types have the same elements example : T1 ≃ Nat := .refl _ example : T2 ≃ Nat := .refl _ example : T3 ≃ Nat := .refl _ example : T4 ≃ Nat := T4_def ▸ .refl _ example : T5 ≃ Nat := ⟨T5.val, T5.mk, fun _ => rfl, fun _ => rfl⟩
wow, thanks for the clarification. I didn't know there are so many layers of "equality". Still one thing is strange:
example {α} (h : Nat = α) : T3 = α := by simp only [h] -- failed
example {α} (h : Nat = α) : T3 = α := h -- worked
If seems definitional equality implies reducible equality?
Before, I only vaguely have two kinds of equality: the '=' in Agda is like the syntactic equality here, and '≡' in Agda the definitional equality here. And also the equivalence in both Agda and here.
Xiaoning Bian (Jan 15 2024 at 05:45):
Mario Carneiro said:
4) I just checked, using 'def N := Nat' won't let you do pattern matching on N. So why are we using and allowing 'def N := Nat'? what's the purpose of 'def N := Nat'? what's the constructor of N?
You can do pattern matching on
N
, you just can't use0
and+1
because these are generic operators and will cause instance search forOfNat
andHAdd
respectively, which will fail on the new type (unless you define these, of course). If you useNat.zero
andNat.succ
it will work
Yeah, it worked. Thanks.
Xiaoning Bian (Jan 15 2024 at 05:47):
Kyle Miller said:
This use of dot means "use the expected type to figure out the namespace for the function". It's able to unfold
def
s while figuring this out.Here's an alternative:
def N := Nat open Nat def add : N → N → N | m, zero => m | m, succ n => succ (add m n)
Or you can write
Nat.zero
andNat.succ
instead of usingopen Nat
.
"use the expected type to figure out the namespace for the function" <--- Nice!! Thanks.
Mario Carneiro (Jan 15 2024 at 05:54):
If seems definitional equality implies reducible equality?
Yes, it's also known as reducible defeq for this reason
Before, I only vaguely have two kinds of equality: the '=' in Agda is like the syntactic equality here, and '≡' in Agda the definitional equality here. And also the equivalence in both Agda and here.
Agda =
is definitely not syntactic equality, but programming languages which are sufficiently restricted in metaprogramming and similar capabilities can sometimes present a notion of equality that is not observably different from syntactic equality
Mario Carneiro (Jan 15 2024 at 05:57):
My guess is that agda =
is most similar to reducible defeq, in that it is internally actually a different type and it is not eagerly expanded by the compiler, but all parts of the system unfold it on demand so as to treat them as equivalent for all user-visible purposes
Xiaoning Bian (Jan 15 2024 at 06:04):
Mario Carneiro said:
My guess is that agda
=
is most similar to reducible defeq, in that it is internally actually a different type and it is not eagerly expanded by the compiler, but all parts of the system unfold it on demand so as to treat them as equivalent for all user-visible purposes
Got it. Thanks. I am more of a user. I come here from Agda for efficency issues. There, a 2^12 case distinction, with each case being an easy computation, takes hours. I am translating my code to Lean4, hoping it works.
Mario Carneiro (Jan 15 2024 at 06:11):
I don't think lean is that great at gigantic inductive types or case distinctions, but it should not be hours
Xiaoning Bian (Jan 15 2024 at 06:13):
Thanks a lot for the help, @everyone. I like here! Unlike the SSprove, I filed an issue 3 months ago on github, still wating for an answer.
Kevin Buzzard (Jan 15 2024 at 07:51):
Re q4: one purpose of allowing def N := Nat
is that now I can define my own <= on N, for example I can define a <= b on N to mean a divides b in Nat.
Xiaoning Bian (Jan 15 2024 at 09:55):
Mario Carneiro said:
If seems definitional equality implies reducible equality?
Yes, it's also known as reducible defeq for this reason
Before, I only vaguely have two kinds of equality: the '=' in Agda is like the syntactic equality here, and '≡' in Agda the definitional equality here. And also the equivalence in both Agda and here.
Agda
=
is definitely not syntactic equality, but programming languages which are sufficiently restricted in metaprogramming and similar capabilities can sometimes present a notion of equality that is not observably different from syntactic equality
Yeah, '=' is not the syntactic equality. 'pattern a = b' is, for which, you can use 'a' for 'b' if 'b' is a constructor when pattern-matching. We cannot if using '='.
Mario Carneiro (Jan 15 2024 at 09:58):
in lean that's the @[match_pattern]
attribute
Markus Schmaus (Jan 17 2024 at 22:41):
Mario Carneiro said:
Here's a demonstration of 5 different kinds of equality in lean and the things that differentiate them:
import Mathlib.Tactic.IrreducibleDef import Mathlib.Logic.Equiv.Basic notation "T1" => Nat abbrev T2 := Nat def T3 := Nat irreducible_def T4 := Nat structure T5 := val : Nat -- syntactic equality means that there is no difference between x0 and x1 def x0 := Nat #print x0 -- def x0 : Type := T1 def x1 := T1 #print x1 -- def x1 : Type := T1 def x2 := T2 #print x2 -- def x2 : Type := T2 def x3 := T3 #print x3 -- def x3 : Type := T3 def x4 := T4 #print x4 -- def x4 : Type := T4 def x5 := T5 #print x5 -- def x5 : Type := T5 -- reducible equality means that tactics like simp will not differentiate them example {α} (h : Nat = α) : T1 = α := by simp only [h] -- ok example {α} (h : Nat = α) : T2 = α := by simp only [h] -- ok example {α} (h : Nat = α) : T3 = α := by simp only [h] -- failed example {α} (h : Nat = α) : T4 = α := by simp only [h] -- failed example {α} (h : Nat = α) : T5 = α := by simp only [h] -- failed -- definitional equality means that rfl proves the equality example : T1 = Nat := rfl -- ok example : T2 = Nat := rfl -- ok example : T3 = Nat := rfl -- ok example : T4 = Nat := rfl -- failed example : T5 = Nat := rfl -- failed -- propositional equality means that the equality is provable example : T1 = Nat := rfl -- ok example : T2 = Nat := rfl -- ok example : T3 = Nat := rfl -- ok example : T4 = Nat := T4_def -- ok example : T5 = Nat := sorry -- not provable -- equivalence means that the types have the same elements example : T1 ≃ Nat := .refl _ example : T2 ≃ Nat := .refl _ example : T3 ≃ Nat := .refl _ example : T4 ≃ Nat := T4_def ▸ .refl _ example : T5 ≃ Nat := ⟨T5.val, T5.mk, fun _ => rfl, fun _ => rfl⟩
This deserves to be somewhere (the Lean manual?) where newbies to Lean can easily find it. I'm learning Lean and reading this was super useful, and I only stumbled upon it by browsing some Zulip threads.
Julian Berman (Jan 17 2024 at 23:43):
Adding them to the glossary as terms seems like a decent idea (as well, not as a replacement)
Last updated: May 02 2025 at 03:31 UTC