Zulip Chat Archive

Stream: new members

Topic: TPIL Chapter 7


Rocky Kamen-Rubio (Mar 13 2020 at 19:20):

  1. I'm running into type issues with inductively defined types that Lean already has defined. For example, when I try running the code from the book below, I get an error in add_zero that n is of type N but type nat is expected.
inductive nat : Type
| zero : nat
| succ : nat  nat

namespace nat
def add (m n : nat) : nat :=
nat.rec_on n m (λ n add_m_n, succ add_m_n)

#reduce add (succ zero) (succ (succ zero))


instance : has_zero nat := has_zero.mk zero --instance is explained in ch 10
instance : has_add nat := has_add.mk add

theorem add_zero (m : nat) : m + 0 = m := rfl
theorem add_succ (m n : nat) : m + succ n = succ (m + n) := rfl --this is how we defined addition


theorem zero_add (n : ) : 0 + n = n :=
nat.rec_on n
  (show 0 + 0 = 0, from rfl)
  (assume n,
    assume ih : 0 + n = n,
    show 0 + succ n = succ n, from
      calc
        0 + succ n = succ (0 + n) : rfl
          ... = succ n : by rw ih)


end nat

Similarly, when I define boolI get the same error when I do bool.cases_on

inductive empty : Type
inductive unit : Type
| star : unit
inductive bool : Type
| ff : bool
| tt : bool

--EXERCISE: Define band bor bnot on this new bool type

def band (b1 b2 : hidden.bool) : hidden.bool :=
bool.cases_on b1 ff b2

def bor (b1 b2 : bool) : bool :=
bool.cases_on b1 b2 tt

def bnot (b : bool) : bool :=
bool.cases_on b tt ff

The same issue comes up when I do sum and prod from the book. I've been copying over the text from the book, and have imported data.nat.basic and tactic

Bryan Gin-ge Chen (Mar 13 2020 at 19:21):

Try adding namespace mydefs before your code and end mydefs after your code. That should help Lean disambiguate the names.

Patrick Massot (Mar 13 2020 at 19:22):

Choosing different names is also a very efficient way to disambiguate.

Bryan Gin-ge Chen (Mar 13 2020 at 19:27):

Note that in zero_add, is notation for the built-in nat (_root_.nat), so it fails.

Bryan Gin-ge Chen (Mar 13 2020 at 19:29):

Not sure why the show fails, but this works:

theorem zero_add (n : nat) : 0 + n = n :=
nat.rec_on n
  (rfl)
  (assume n,
    assume ih : 0 + n = n,
    show 0 + n.succ = n.succ, from
      calc
        0 + n.succ = (0 + n).succ : rfl
          ... = n.succ : by rw ih)

Rocky Kamen-Rubio (Mar 13 2020 at 19:36):

Bryan Gin-ge Chen said:

Note that in zero_add, is notation for the built-in nat (_root_.nat), so it fails.

Interesting, yeah I'm also getting that it fails when I use show but not when I just use (rfl) like you are.

Rocky Kamen-Rubio (Mar 13 2020 at 19:44):

I'm also getting an error on the #check term at the end when I try to do

inductive list (α : Type u)
| nil {} : list
| cons : α  list  list

namespace list
variable {α : Type}
notation h :: t  := cons h t
def append (s t : list α) : list α :=
list.rec t (λ x l u, x::u) s
notation s ++ t := append s t
theorem nil_append (t : list α) : nil ++ t = t := rfl
theorem cons_append (x : α) (s t : list α) :
  x::s ++ t = x::(s ++ t) := rfl
notation '[' l:(foldr ',' (h t, cons h t) nil) ']' := l

#check ([1,2,3] : list )

--EDIT: It only happens when I open a new section like this
section
    open nat
    #check [1,2,3,4,5]
    #check [1,2,3] : list 
end

Rocky Kamen-Rubio (Mar 17 2020 at 01:23):

I'm very confused by the exercises suggested at the end of section 7.2. They read

As exercises, we encourage you to develop a notion of composition for partial functions from α to β and β to γ, and show that it behaves as expected. We also encourage you to show that bool and nat are inhabited, that the product of two inhabited types is inhabited, and that the type of functions to an inhabited type is inhabited.

inductive option (α : Type u)
| none {} : option
| some    : α  option

inductive inhabited (α : Type u)
| mk : α  inhabited

For the inhabited type, I understand conceptually that given an example of an instance of a type, we could generate an object that says this type is inhabited. I'm at a loss for how to actually do something like define inhabited_example ... the we the book does for sum and prod.

For option, I'm substantially more confused. It seems like option would go from a given element of the domain to either none or to a defined function, not the other way around. I feel like I'm misunderstanding something about what it means to have a recursively defined type.

Mario Carneiro (Mar 17 2020 at 01:50):

option A is a type which has one more element than A. The extra element is called none : option A, and the function that maps A into option A is called some : A -> option A.

Mario Carneiro (Mar 17 2020 at 01:53):

Can you prove

example {A B} (h1 : inhabited A) (h2 : inhabited B) : inhabited (A × B) := sorry

Rocky Kamen-Rubio (Mar 17 2020 at 02:19):

Mario Carneiro said:

Can you prove

example {A B} (h1 : inhabited A) (h2 : inhabited B) : inhabited (A × B) := sorry

I'm not sure... If we have proofs that A and B and inhabited, it makes sense me that A x B would be inhabited. My intuition is to apply inhabited.mk to the product of the two instances we have, but that gives me a type error (understandably). Is there a way to extract the actual instance from an inhabited object so we can use it to make a new one?

example {A B} (h1 : inhabited A) (h2 : inhabited B) : inhabited (A × B) :=
inhabited.mk (h1 × h2)

Bryan Gin-ge Chen (Mar 17 2020 at 02:34):

(deleted, I was using Lean's definition of inhabited, not the one given by TPiL...)

Bryan Gin-ge Chen (Mar 17 2020 at 03:03):

One way is to use the match ... with ... end syntax:

example {A B} (h1 : inhabited A) (h2 : inhabited B) : inhabited (A × B) :=
match h1, h2 with
| (inhabited.mk a), (inhabited.mk b) := inhabited.mk a, b
end

This syntax "pattern matches" on h1 and h2 to pull out a : A and b : B.

You can also use angle brackets in place of the constructors like this:

example {A B} (h1 : inhabited A) (h2 : inhabited B) : inhabited (A × B) :=
match h1, h2 with
| a, b := ⟨⟨a, b⟩⟩
end

Bryan Gin-ge Chen (Mar 17 2020 at 03:07):

Compare also:

example {A B} (h1 : inhabited A) (h2 : inhabited B) : inhabited (A × B) :=
let a := h1 in
let b := h2 in
⟨⟨a, b⟩⟩

example {A B} : inhabited A  inhabited B  inhabited (A × B)
| a b := ⟨⟨a, b⟩⟩

Bryan Gin-ge Chen (Mar 17 2020 at 03:29):

Oh, pattern-matching is introduced in chapter 8, so I guess you're meant to use inhabited.cases_on instead. Here's a hint:

example {A B} (h1 : inhabited A) (h2 : inhabited B) : inhabited (A × B) :=
inhabited.cases_on h1 (λ a, inhabited.cases_on h2 (λ b, _))

[I was hoping this would work with dot notation (so I could write h1.cases_on and h2.cases_on instead of inhabited.cases_on h1, etc.) but it looks like that's too much for the elaborator...]

Rocky Kamen-Rubio (Mar 18 2020 at 05:03):

Bryan Gin-ge Chen said:

Oh, pattern-matching is introduced in chapter 8, so I guess you're meant to use inhabited.cases_on instead. Here's a hint:

example {A B} (h1 : inhabited A) (h2 : inhabited B) : inhabited (A × B) :=
inhabited.cases_on h1 (λ a, inhabited.cases_on h2 (λ b, _))

[I was hoping this would work with dot notation (so I could write h1.cases_on and h2.cases_on instead of inhabited.cases_on h1, etc.) but it looks like that's too much for the elaborator...]

Thanks for your detailed answer! I'm getting a "don't know how to synthesize placeholder
context:" error on the wildecard. Is there something I need to import somewhere? I'm also still a little confused conceptually by how this works but maybe that'll be cleared up after reading chapter 8.

Rocky Kamen-Rubio (Mar 18 2020 at 05:07):

I think part of my confusion comes from the fact that all the solutions you gave rely on implicit notation using either brackets or wildcards. Is there an explicit way to show a type is inhabited, or is that not the way I should be thinking about this problem?

Bryan Gin-ge Chen (Mar 18 2020 at 05:12):

I'm getting a "don't know how to synthesize placeholder context:" error on the wildecard.

That's right, the underscore was left for you to fill in. :wink:

Is there something I need to import somewhere?

No imports necessary!

Is there an explicit way to show a type is inhabited

Yep, in this context, ⟨⟨a, b⟩⟩ is just shorthand for to inhabited.mk (prod.mk a b).

Rocky Kamen-Rubio (Mar 18 2020 at 05:14):

Oh, that makes sense haha. I thought you were intentionally leaving it as a wildcard. This is very helpful, thank you!

Rocky Kamen-Rubio (Mar 18 2020 at 05:56):

Ok I'm still a little confused about how option works. I'm trying to define a simple function that is the identity on 0 and 1, and undefined everywhere else. I'm getting a type mismatch error on all three := signs when I do the following. Any thoughts?

def partial :   option 
| 0 := some 0
| 1 := some 1
| _ := none

Kenny Lau (Mar 18 2020 at 06:01):

it does not give any error for me.

Bryan Gin-ge Chen (Mar 18 2020 at 06:06):

Rocky's using the option defined in TPiL, so the full code is something like this:

namespace blah
universe u
inductive option (α : Type u)
| none {} : option
| some    : α  option


def partial :   option 
| 0 := some 0
| 1 := some 1
| _ := none

end blah

Bryan Gin-ge Chen (Mar 18 2020 at 06:06):

The solution is to add open option before def partial.

This works:

namespace blah
universe u
inductive option (α : Type u)
| none {} : option
| some    : α  option

def partial :   option 
| 0 := option.some 0
| 1 := option.some 1
| _ := option.none

end blah

Rocky Kamen-Rubio (Mar 18 2020 at 06:08):

Thank you @Bryan Gin-ge Chen for catching that! Adding open option doesn't seem to resolve this issue. Leaving the namespace and using lean's default option does work though. I thought the two definitions were the same here... is that not the case?

Rocky Kamen-Rubio (Mar 18 2020 at 06:09):

Bryan Gin-ge Chen said:

The solution is to add open option before def partial.

This works:

namespace blah
universe u
inductive option (α : Type u)
| none {} : option
| some    : α  option

def partial :   option 
| 0 := option.some 0
| 1 := option.some 1
| _ := option.none

end blah

That did the trick. Thank you!

Bryan Gin-ge Chen (Mar 18 2020 at 06:11):

Yeah, it wasn't open option that worked but rather surrounding def partial with namespace option and end option (or writing the names out fully as I did above).

Bryan Gin-ge Chen (Mar 18 2020 at 06:12):

The option provided by Lean's core library is equivalent, but you can't use it when Lean is expecting your option.

Rocky Kamen-Rubio (Mar 18 2020 at 06:14):

Bryan Gin-ge Chen said:

The option provided by Lean's core library is equivalent, but you can't use it when Lean is expecting your option.

Makes sense. This has come up before when TPIL makes you define types that are built in and I've forgotten that the interactions are a little different. Thanks!

Rocky Kamen-Rubio (Mar 18 2020 at 06:14):

Another question: I'm trying to define the composite of partial functions also discussed in this section. I have this so far, and am (understandably) running into an error when I try to apply g to (f a) because it is an element of option β rather than β. Is there an easy way to convert back from an option type to the element of the regular type?

def compose_partials {α β γ: Type} (f : α  option β) (g : β  option γ) : α  option γ  :=
λ a : α, option.cases_on (f a) (some (g (f a))) (none)

Shing Tak Lam (Mar 18 2020 at 06:27):

I think you might need a helper function like this:

def helper {α : Type} : option (option α) -> option α
| (some (some x)) := some x
| _               := none

I don't think there is a way to have a function wheref : option a -> a in Lean, since f none would require you to be able to make a value of type a for all a.

In other languages (Haskell, etc.) there is what you're looking for, but they crash on the case f none.

Nevermind.

Bryan Gin-ge Chen (Mar 18 2020 at 06:31):

This works:

def compose_partials {α β γ: Type} (f : α  option β) (g : β  option γ) : α  option γ :=
λ a : α, option.cases_on (f a) none g

I think maybe you were thrown off by having none in the wrong slot.

Kevin Buzzard (Mar 18 2020 at 07:38):

I think this is called bind and it's basically the proof that option is a monad

Rocky Kamen-Rubio (Mar 19 2020 at 03:17):

Bryan Gin-ge Chen said:

This works:

def compose_partials {α β γ: Type} (f : α  option β) (g : β  option γ) : α  option γ :=
λ a : α, option.cases_on (f a) none g

I think maybe you were thrown off by having none in the wrong slot.

Wouldn't this just give us the function g in the case that a is in the domain of f, and not the element that g maps (f a) to? Or am I missing something?

Rocky Kamen-Rubio (Mar 19 2020 at 03:19):

Shing Tak Lam said:

I think you might need a helper function like this:

def helper {α : Type} : option (option α) -> option α
| (some (some x)) := some x
| _               := none

I don't think there is a way to have a function wheref : option a -> a in Lean, since f none would require you to be able to make a value of type a for all a.

In other languages (Haskell, etc.) there is what you're looking for, but they crash on the case f none.

Nevermind.

That makes sense. The notation for more abstract recursive types like this still makes my head hurt a little, but I feel like that will probably get better with time.

Reid Barton (Mar 19 2020 at 03:26):

It might help to see the definition expanded as λ a : α, option.cases_on (f a) none (λ b, g b)

Rocky Kamen-Rubio (Mar 19 2020 at 03:29):

Reid Barton said:

It might help to see the definition expanded as λ a : α, option.cases_on (f a) none (λ b, g b)

Using a helper function and reversing the order of the arguments to option (f a) _ _ I still get two errors.

That makes sense to me. Does cases_on do that automatically with the notation @Bryan Gin-ge Chen gave?

Reid Barton (Mar 19 2020 at 03:30):

I don't understand your question. But g equals λ b, g b.

Rocky Kamen-Rubio (Mar 19 2020 at 03:33):

Reid Barton said:

I don't understand your question. But g equals λ b, g b.

Oh duh, yeah I was just getting a little lost in what was taking in what and going where with cases_on. Yeah that makes sense now.

@Shing Tak Lam I tried doing something like this and got rid of the return type error but I'm getting type mismatches in my helper function and at option.cases_on

def helper {α : Type} (input : option α): α
| some (some x) : some x
| some x : x
| _ : none

def compose_partials {α β γ: Type} (f : α  option β) (g : β  option γ) : α  option γ  :=
λ a : α, option.cases_on (f a) none (λ b : β, helper (g b))

Shing Tak Lam (Mar 19 2020 at 04:25):

@Rocky Kamen-Rubio

Nevermind what I said previously. I didn't look at it properly. (I never did TPIL). So I did it with option.map instead of option.cases_on.

Sorry for any confusion

Kevin Buzzard (Mar 19 2020 at 07:48):

@ROCKY KAMEN-RUBIO your helper definition does not look good to me. What is the type of x supposed to be in some (some x)? You say you're getting type mismatches but if you just read them carefully they will tell you by themselves the errors you made

Kevin Buzzard (Mar 19 2020 at 07:49):

You do understand that alpha and option alpha are completely different types, right?

Kevin Buzzard (Mar 19 2020 at 07:50):

There is no natural helper map of the kind you're trying to define because where is none going to go? Alpha could be empty

Kevin Buzzard (Mar 19 2020 at 07:52):

Maybe you should take a look at learnyouahaskell? This taught me a lot about functional programming

Mario Carneiro (Mar 19 2020 at 07:58):

Given the type of compose_partials, it looks like the right thing to put in the location of helper is the identity function

Shing Tak Lam (Mar 19 2020 at 08:31):

@Rocky Kamen-Rubio

The helper function I posted is

def helper {α : Type} : option (option α) -> option α
| (some (some x)) := some x
| _               := none

The one you posted is below

def helper {α : Type} (input : option α): α
| some (some x) : some x
| some x : x
| _ : none

Notice how mine is option (option α) -> option α, while yours is option α -> α. That is why the first case from mine (some (some x)) := some x doesn't apply for you, as the left hand side (some (some x)) has the type option (option α), not option α. Also, the parenthesis matter here, as when used to pattern match, (some (some x)) and some (some x) are different.

Additionally, there is a syntax error there, it should be := in the cases.

Also notice the types of your cases do not match, as the first one is option (option α) -> option α, the second one is option α -> α, the last one is option α -> option ???. So lean complains that all of them have different types.

The helper function was when I solved it with option.map, and option.cases_on expects a different function, so that's why you're getting an error there. option.map would produce a option (option α), which is why I needed the helper function, as for this example, some (none) is pragmatically the same as none, so I use _ to catch both cases.

Sorry for any confusion caused

Lucas Teixeira (Sep 21 2021 at 09:39):

I'm having a some issues understanding how the book's definition of natural number addition works.

namespace hidden

inductive myNat : Type
  | zero : myNat
  | succ : myNat  myNat

def add (m n: myNat) : myNat :=
  myNat.rec_on m n (λ m add_m_n, myNat.succ add_m_n)

end hidden

My understanding of addition in peano arithmetic is the following:

0 + n : = n
(m++) + n := (m + n)++

I 'm a little confused on the mechanics of rec_on and how add_m_n is evaluated as m + n

Lucas Teixeira (Sep 21 2021 at 09:51):

Also, this type reading

#check @nat.rec_on

Π {C : nat  Type*} (n : nat),
  C nat.zero  (Π (a : nat), C a  C (nat.succ a))  C n

is completely opaque to me

Yaël Dillies (Sep 21 2021 at 09:54):

This is exactly what this definition is doing. myNat.rec_on takes in

  • The variable to recurse on: m
  • The value you want to output when the variable is 0: n because 0 + n = n
  • The value you want to output when the variable is succ a given the value for a: succ (a + n) because succ a + n = succ (a + n).

Yaël Dillies (Sep 21 2021 at 09:55):

Maybe it's a bit clearer if you write it

inductive myNat : Type
  | zero : myNat
  | succ : myNat  myNat

def add (m n: myNat) : myNat :=
  myNat.rec_on m n (λ a add_a_n, myNat.succ add_a_n)

to make clear those two m don't have the same meaning.

Yaël Dillies (Sep 21 2021 at 10:02):

The point of a recursor of an inductive type α is to define a function α → β. To define a function from α, you only need to define that function for every constructor of α. nat has two constructors: zero andsucc, so to define C : nat → Type* you only need to define C 0 and C a → C (nat.succ a) for all a. This is exactly what nat.rec_on does:
Π {C : nat → Type*} (n : nat), C nat.zero → (Π (a : nat), C a → C (nat.succ a)) → C n
(Π is a synonym of used when you're defining data, but they're the same in practice)
"For all function C from nat to anything,
if I know C 0
and if for all a : nat I know C (succ a) from C a,
then I know C n for all n : nat."

Yaël Dillies (Sep 21 2021 at 10:03):

Note that n is bound earlier than in my translation. That's the (only) difference between nat.rec and nat.rec_on: the order of the arguments.

Lucas Teixeira (Sep 21 2021 at 10:19):

Yaël Dillies said:

So in the case of add, would add_a_n be the C a in Π {C : nat → Type*} (n : nat), C nat.zero → (Π (a : nat), C a → C (nat.succ a)) → C n ??

Yaël Dillies (Sep 21 2021 at 10:23):

Exactly! It corresponds to the value you already assigned to a + n.

Lucas Teixeira (Sep 21 2021 at 10:54):

Yaël Dillies
This is starting to make a lot more sense now.

But I'm still a little lost for when exactly I assigned the value of a + n. The type signature seems to take it as an assumption.

I know I assigned zero.
And I know if we recurse on any n ≠ zero we'll eventually hit zero.
And I understand that since C: myNat → myNat and succ : myNat → myNat C succ doesn't type check and we're required to use C (nat.succ a) where a : myNat and to define this we'd have to know C a for all a.

But I don't see where the proof for C a is actually constructed. Is this the part of a recursive definition where we are defining something in terms of itself?

Yaël Dillies (Sep 21 2021 at 11:38):

Your question boils down to "Where is the n in succ n coming from?". The answer is that succ : nat → nat, so you're given n : nat to get succ n : nat. With C, precisely the same thing happens. You're given a : nat and C a and you must produce C (succ a).

Yaël Dillies (Sep 21 2021 at 11:46):

You can think of h0 : C 0 and hsucc : Π (a : nat), C a → C (succ a) as instructions to compute C. If I want to compute C 3 = C (succ (succ (succ 0))), then it will be hsucc 2 (hsucc 1 (hsucc 0 h0))).

Lucas Teixeira (Sep 21 2021 at 12:18):

Yaël Dillies
Okay, I think I'm getting it now.

So for the purposes of demonstration let's say that n: nat and n': nat

The a in C a in (Π (a : nat), C a → C (nat.succ a)) by definition of nat is either zero or succ n': nat. This means that nat.succ a is either nat.succ zero or nat.succ (nat.succ n') respectively.

Eric Wieser (Sep 21 2021 at 12:22):

I'm not sure that observation is interesting or helpful to your confusion, but it is correct

Filippo A. E. Nuccio (Oct 01 2021 at 10:50):

I am trying to understand this more and more, and I am realising that the definition

#check @nat.rec_on

Π {C : nat  Type*} (n : nat),
  C nat.zero  (Π (a : nat), C a  C (nat.succ a))  C n

is actually defining a family of types indexed by nat, one C n : Type* for each n : nat(actually, on my pc I see C : nat → Sort, but this makes no difference for my question). Yet then when I try to understand what goes beyond the following

def foo (n : nat) : nat := nat.rec_on n (zero : nat) (λ n m, (zero : nat))

(which I intuitively view as the constant function 00 defined inductively on n), I am perplexed: how is it possible that Lean accepts (zero : nat) for the type C zero? I am happy to see types as being terms of Type1 but is it also the case that every term like zero : nat is a Type?

Yaël Dillies (Oct 01 2021 at 10:52):

Welcome! :wave:

Yaël Dillies (Oct 01 2021 at 10:53):

In the case of usual induction C n is of type Prop everywhere. That's how I understand it.

Eric Rodriguez (Oct 01 2021 at 10:53):

I think it's a bit clearer what's going on you write it out with @:

def foo (n : nat) : nat := @nat.rec_on (λ _, nat) n (zero : nat) (λ n m, (zero : nat))

Ruben Van de Velde (Oct 01 2021 at 10:54):

Note that (zero : nat) is not C 0, it's a value of type C 0, i.e., a value of type nat

Ruben Van de Velde (Oct 01 2021 at 10:55):

(This code also breaks my brain, fwiw)

Filippo A. E. Nuccio (Oct 01 2021 at 10:58):

Well, but if C takes values in Type* it cannot take values in nat, right?

Kevin Buzzard (Oct 01 2021 at 10:58):

C is the type of what you're making, so C 0 = nat

Kevin Buzzard (Oct 01 2021 at 10:58):

and nat : Type

Filippo A. E. Nuccio (Oct 01 2021 at 10:59):

Do you mean C or the value of C at some n : nat is the type of what I am making?

Ruben Van de Velde (Oct 01 2021 at 10:59):

C is a function that takes a natural number to a type (* Sort)

Ruben Van de Velde (Oct 01 2021 at 10:59):

So C 0 is a type

Filippo A. E. Nuccio (Oct 01 2021 at 10:59):

Ah, ok, and it is nat.

Kevin Buzzard (Oct 01 2021 at 10:59):

Induction in this generality starts with a function C : nat -> Type or C : nat -> Prop, and then proceeds to construct elements of C n for all n from the usual 0 and succ hypotheses

Yaël Dillies (Oct 01 2021 at 11:00):

In your case, C := λ n, ℕ.

Filippo A. E. Nuccio (Oct 01 2021 at 11:00):

Oh, yes, exactly, thanks!

Kevin Buzzard (Oct 01 2021 at 11:00):

If you're constructing data, C is often constant. If you're doing induction then C is never constant, e.g. C n can be the statement i=1ni=n(n+1)/2\sum_{i=1}^ni=n(n+1)/2

Yaël Dillies (Oct 01 2021 at 11:00):

You don't actually need recursion in your case. That may have obscured the situation.

Filippo A. E. Nuccio (Oct 01 2021 at 11:01):

Well, no: my point was not to understand how to construct the constant function, but to understand subtleties about rec_on...

Ruben Van de Velde (Oct 01 2021 at 11:02):

A case with a non-constant C that I found easier to wrap my head around is vector T n, the lists of length n

Kevin Buzzard (Oct 01 2021 at 11:02):

What is your question, based on your revised understanding of C?

Filippo A. E. Nuccio (Oct 01 2021 at 11:03):

Well, none any more: I was looking for a revised understanding of C and I got it. So I am happy. Does it count as a question?

Yaël Dillies (Oct 01 2021 at 11:03):

What makes you happy?

Filippo A. E. Nuccio (Oct 01 2021 at 11:03):

Ruben Van de Velde said:

A case with a non-constant C that I found easier to wrap my head around is vector T n, the lists of length n

Thanks, I'll think about it.

Kevin Buzzard (Oct 01 2021 at 11:04):

The Sort* thing means "either Prop or Type" i.e. one function which does both induction and recursion

Filippo A. E. Nuccio (Oct 01 2021 at 11:04):

Yaël Dillies said:

What makes you happy?

Basically that I understood my mistake, no? I had not been able to figure out that C := λ n, ℕ.

Filippo A. E. Nuccio (Oct 01 2021 at 11:05):

I apologize if my satisfaction is a low hanging fruit... :wink:

Kevin Buzzard (Oct 01 2021 at 11:06):

I also remember being super-confused about all this

Yaël Dillies (Oct 01 2021 at 11:06):

What is happiness?

Filippo A. E. Nuccio (Oct 01 2021 at 11:08):

Well, I don't know if this counts as happiness, but I have tried to insert your suggestion in the code and I got very happy:

#check @nat.rec_on (λ n, nat)

gives

nat.rec_on :
  Π (n : nat),
    (λ (n : nat), nat) zero 
    (Π ( : nat), (λ (n : nat), nat)   (λ (n : nat), nat) ᾰ.succ)  (λ (n : nat), nat) n

Filippo A. E. Nuccio (Oct 01 2021 at 11:08):

And I find it very much clear!

Filippo A. E. Nuccio (Oct 01 2021 at 11:11):

No, really: I was always a bit perplexed of the syntax of the second term of an inductively defined gadget, which goes as (λ n m, (zero : nat)) whereas I would have thought that (λ m, (zero : nat)) to mean "if n = succ m, then send it to zero" would have been more reasonable. But with the above code it is clear that the lambda expression must also contain n.

Eric Wieser (Oct 01 2021 at 11:47):

You can also do:

def type_of {α} (a : α) := α
#reduce type_of (@nat.rec_on (λ n, nat))  -- `ℕ → ℕ → (ℕ → ℕ → ℕ) → ℕ`

but the problem with non-dependent binders is that you lose all the names!

Filippo A. E. Nuccio (Oct 01 2021 at 12:03):

Thanks! I agree that one loses all names, but it is at any rate useful to have a clear picture of what goes on. :pray:

Ayush Agrawal (May 02 2022 at 10:30):

I defined len of the list definition as follows:
def len {α : Type*} (n : list2 α ) : ℕ := list2.rec_on n 0 (λ x l leni, leni+1)
However, I am not able to prove a basic statement :
theorem list_len_one {α : Type*} (x : α ) (g : list2 α ) : len (x::g) = 1 + len g :=
Can someone help me to prove this? thanks!

Yaël Dillies (May 02 2022 at 10:31):

Does add_comm _ _ work as a proof?

Ayush Agrawal (May 02 2022 at 10:40):

Oh I see now, thanks @Yaël Dillies ,
theorem list_len_one {α : Type*} (x : α ) (g : list2 α ) : len (x::g) = 1 + len g := begin rw nat.add_comm _ _, apply rfl, end
this now works!

Yaël Dillies (May 02 2022 at 10:41):

Yes, because you wrote leni+1, not 1+leni :smile:

Kevin Buzzard (May 02 2022 at 10:46):

This is very easy to do if you're coming into this area with a mathematical background, because a+b=b+a feels like it should be true by definition and before you start formalising you treat these two terms as the same.

Kevin Buzzard (May 02 2022 at 10:48):

In maths we don't think twice about going from n(an×C)\sum_n (a_n\times C) to CnanC\sum_n a_n but in Lean this is two commuting steps: take the factor out the sum, and rearrange.


Last updated: Dec 20 2023 at 11:08 UTC