Zulip Chat Archive
Stream: general
Topic: Fresh off the boat
Nima (Mar 29 2018 at 04:15):
Anyone is willing to prove this example for me? It seems too obvious, but I have not luck doing it within the past hour.
universe u variable {α : Type u} structure constraint (α:Type u) := cnstr:: (trv:bool) -- is trivial (stt:bool) -- is strict (low:bool) -- is lower-bound (bnd:α) open constraint def setof : constraint α → α → Prop | (cnstr tt _ _ _ ) _ := tt | (cnstr _ tt low bnd) a := tt --if low then bnd < a else a < bnd | (cnstr _ ff low bnd) a := tt --if low then bnd ≤ a else a ≤ bnd example (c: constraint α) (a:α) : setof c a := sorry
Kenny Lau (Mar 29 2018 at 04:17):
It doesn't even typecheck @Nima
Nima (Mar 29 2018 at 04:19):
@Kenny Lau Sorry, I added "open constraint". Now it is typed checked
Simon Hudon (Mar 29 2018 at 04:20):
you can prove it as:
example (c: constraint α) (a:α) : setof c a := by cases c ; trivial
I believe
Nima (Mar 29 2018 at 04:23):
@Simon Hudon internal.lean:21:59: error
trivial tactic failed
state:
α : Type u,
a : α,
trv stt low : bool,
bnd : α
⊢ setof {trv := trv, stt := stt, low := low, bnd := bnd} a
Mario Carneiro (Mar 29 2018 at 04:27):
You probably want true
instead of tt
in the definition of setof
Simon Hudon (Mar 29 2018 at 04:30):
And you may find it easier to use this definition rather than yours:
inductive constraint (a : Type u) | trivial : constraint | strict_upper (x : a) : constraint | strict_lower (x : a) : constraint | nonstrict_upper (x : a) : constraint | nonstrict_lower (x : a) : constraint
Nima (Mar 29 2018 at 04:30):
@Mario Carneiro
The following gives me the same error
def setof : constraint α → α → Prop | (cnstr tt _ _ _ ) _ := true | (cnstr _ tt low bnd) a := true --if low then bnd < a else a < bnd | (cnstr _ ff low bnd) a := true --if low then bnd ≤ a else a ≤ bnd
The following is not typechecked
def setof : constraint α → α → Prop | (cnstr true _ _ _ ) _ := true | (cnstr _ true low bnd) a := true --if low then bnd < a else a < bnd | (cnstr _ false low bnd) a := true --if low then bnd ≤ a else a ≤ bnd
Mario Carneiro (Mar 29 2018 at 04:31):
The first one is what I mean. FYI you can code block with triple backquote before and after
Mario Carneiro (Mar 29 2018 at 04:32):
def setof : constraint α → α → Prop | (cnstr tt _ _ _ ) _ := true | (cnstr ff tt low bnd) a := true --if low then bnd < a else a < bnd | (cnstr ff ff low bnd) a := true --if low then bnd ≤ a else a ≤ bnd example : ∀ (c : constraint α) (a:α), setof c a | (cnstr tt _ _ _) _ := trivial | (cnstr ff tt low bnd) a := trivial | (cnstr ff ff low bnd) a := trivial
Kenny Lau (Mar 29 2018 at 04:33):
@Mario Carneiro you don't need after :P
Mario Carneiro (Mar 29 2018 at 04:33):
Are you one of those people who leaves off </html>
Mario Carneiro (Mar 29 2018 at 04:33):
YOU RUINED THE INTERNET
Nima (Mar 29 2018 at 04:35):
@Mario Carneiro Not sure what you mean!?
Mario Carneiro (Mar 29 2018 at 04:35):
About what? The example shows how to prove the theorem by cases
Mario Carneiro (Mar 29 2018 at 04:36):
my quip about the internet was for kenny's benefit
Mario Carneiro (Mar 29 2018 at 04:36):
Note that you need to explicitly specify ff
for the latter two cases in the definition of setof
for the proof to work
Nima (Mar 29 2018 at 04:37):
@Mario Carneiro Thanks a lot, it worked
Mario Carneiro (Mar 29 2018 at 04:38):
Also, if you want to use <
you will need an appropriate typeclass, e.g.
def setof [linear_order α] : constraint α → α → Prop | (cnstr tt _ _ _ ) _ := true | (cnstr ff tt low bnd) a := if low then bnd < a else a < bnd | (cnstr ff ff low bnd) a := if low then bnd ≤ a else a ≤ bnd
Nima (Mar 29 2018 at 04:39):
@Mario Carneiro too soon for those lessons ;)
Mario Carneiro (Mar 29 2018 at 04:40):
I agree with simon though about your encoding; having a bunch of bool
flags will make things harder than just having a single inductive case split
Mario Carneiro (Mar 29 2018 at 04:43):
You can also replace α → Prop
with set α
in the definition of setof
(they are the same, but set α
has more associated notations)
Nima (Mar 29 2018 at 04:43):
@Mario Carneiro Is there any easy explanation for why do I need "to explicitly specify ff
for the latter two cases in the definition of setof
for the proof to work "
Simon Hudon (Mar 29 2018 at 04:44):
And if you want to avoid type classes, you should pick a specific type (e.g. ℕ
, ℤ
, ℚ
, ℝ
) and the order will come from there
Simon Hudon (Mar 29 2018 at 04:46):
@Mario Carneiro Is there any easy explanation for why do I need "to explicitly specify
ff
for the latter two cases in the definition ofsetof
for the proof to work "
Because your definition has three equations and you can't match against the bools unless you know their values because you use their exact values in the equations
Simon Hudon (Mar 29 2018 at 04:46):
E.g.:
| (cnstr tt _ _ _ ) _ := true
Mario Carneiro (Mar 29 2018 at 04:47):
def setof : constraint α → α → Prop | (cnstr tt _ _ _ ) _ := true | (cnstr ff tt low bnd) a := true | (cnstr ff ff low bnd) a := true #print prefix setof.equations -- setof.equations._eqn_1 : ∀ {α : Type u} (low : bool) (bnd a : α), setof {trv := ff, stt := ff, low := low, bnd := bnd} a = true -- setof.equations._eqn_2 : ∀ {α : Type u} (low : bool) (bnd a : α), setof {trv := ff, stt := tt, low := low, bnd := bnd} a = true -- setof.equations._eqn_3 : ∀ {α : Type u} (_x _x_1 : bool) (_x_2 _x_3 : α), setof {trv := tt, stt := _x, low := _x_1, bnd := _x_2} _x_3 = true def setof : constraint α → α → Prop | (cnstr tt _ _ _ ) _ := true | (cnstr _ tt low bnd) a := true | (cnstr _ ff low bnd) a := true #print prefix setof.equations -- setof.equations._eqn_1 : ∀ {α : Type u} (low : bool) (bnd a : α), setof {trv := ff, stt := ff, low := low, bnd := bnd} a = true -- setof.equations._eqn_2 : ∀ {α : Type u} (low : bool) (bnd a : α), setof {trv := ff, stt := tt, low := low, bnd := bnd} a = true -- setof.equations._eqn_3 : ∀ {α : Type u} (_x : bool) (_x_1 _x_2 : α), setof {trv := tt, stt := ff, low := _x, bnd := _x_1} _x_2 = true -- setof.equations._eqn_4 : ∀ {α : Type u} (_x : bool) (_x_1 _x_2 : α), setof {trv := tt, stt := tt, low := _x, bnd := _x_1} _x_2 = true
Mario Carneiro (Mar 29 2018 at 04:48):
Actually, the problem is that without specifying that the last two cases are ff
, it does its first case split on the second bool, leading to four cases overall (with a superfluous case split on stt
in the trivial case). This means that later you will need to case on it in the theorem
Nima (Mar 29 2018 at 04:49):
@Mario Carneiro So the following from "8.2. Wildcards and Overlapping Patterns" is not that useful " But Lean handles the ambiguity by using the first applicable equation, so the net result is the same "
Mario Carneiro (Mar 29 2018 at 04:50):
This has more to do with internals of the heuristics for what to case split when you aren't being explicit about it
Nima (Mar 29 2018 at 04:50):
@Mario Carneiro Got it! Thanks
Mario Carneiro (Mar 29 2018 at 04:51):
There's really no need to use a variable in the latter cases, you know it's ff
and by writing it you tell lean to split there
Nima (Mar 29 2018 at 14:44):
Have I defined has_coe_to_sort
correctly? If so, how do I fix the admit
s in my proof?
inductive constraint | trv : constraint | stt (bnd:ℕ) (low:Prop) : constraint | nst (bnd:ℕ) (low:Prop) : constraint namespace constraint def setof : constraint → ℕ → Prop := begin intros c a, cases c with bnd lft bnd lft, case trv {exact true}, case stt bnd lft {exact lft ∧ bnd<a ∨ a < bnd}, case nst bnd lft {exact lft ∧ bnd≤a ∨ a ≤ bnd}, end instance constraint_to_sort : has_coe_to_sort constraint := {S := Type, coe := λ S, {x // setof S x}} example : ∀ (c:constraint) (n:c), setof c n := begin intros c n, cases c, trivial, begin admit end, begin admit end end end constraint
Kevin Buzzard (Mar 29 2018 at 15:36):
This doesn't typecheck for me.
Kevin Buzzard (Mar 29 2018 at 15:36):
invalid `case`, there is no goal tagged with prefix [constraint.stt, bnd, lft] state: 2 goals case constraint.stt a bnd : ℕ, lft : Prop ⊢ Prop case constraint.nst a bnd : ℕ, lft : Prop ⊢ Prop
Kevin Buzzard (Mar 29 2018 at 15:36):
line 13
Kevin Buzzard (Mar 29 2018 at 15:40):
fixed it
Kevin Buzzard (Mar 29 2018 at 15:41):
def setof : constraint → ℕ → Prop := begin intros c a, cases c with bnd lft bnd lft, case trv {exact true}, case stt {exact lft ∧ bnd<a ∨ a < bnd}, case nst {exact lft ∧ bnd≤a ∨ a ≤ bnd}, end
Kevin Buzzard (Mar 29 2018 at 15:41):
I'm using a recent lean nightly
Nima (Mar 29 2018 at 15:43):
@Kevin Buzzard
The following two lines fixed it (replace admit
s)
exact @subtype.property nat (setof (stt bnd low)) n
exact @subtype.property nat (setof (nst bnd low)) n
In fact I can solve the example in the following way
example : ∀ (c:constraint) (n:c), setof c n := begin intros c n, exact @subtype.property nat (setof c) n end
Kevin Buzzard (Mar 29 2018 at 15:47):
I am confused about what's going on.
Kevin Buzzard (Mar 29 2018 at 15:47):
setof c
wants a nat, and you give it n
, which is a c
.
Nima (Mar 29 2018 at 17:14):
Why the following is invalid?
Error Message " invalid universe declaration, 'u_1' shadows a local universe"
If I use variable α : Type u
then everything will be fine
universe u constant α : Type u variables x y : α
The following gives me no error (why?):
universe u constant α : Type u variable x : α variable y : α
Nima (Mar 29 2018 at 17:43):
I know the following example can be solve by replacing the last line with exact hm
. But why I receive a this error:
universe u variable {α : Type u} constant le : α → α → Prop example : ∀ (α : Type u) (hm : ∃ (m : α), ∀ (m' : α), le m m'), (∃ (m : α), ∀ (a : α) , le m a) := begin intros α hm, match hm with ⟨m,hh⟩ := sorry end
It gives me
tmp.lean:12:2: error don't know how to synthesize placeholder context: ⊢ Type ? tmp.lean:12:2: error equation compiler failed (use 'set_option trace.eqn_compiler.elim_match true' for additional details) tmp.lean:12:8: error unknown identifier 'hm' tmp.lean:12:8: error don't know how to synthesize placeholder context: ⊢ Sort ? tmp.lean:12:16: error invalid constructor ⟨...⟩, expected type is not an inductive type ?m_1
Kevin Buzzard (Mar 29 2018 at 17:44):
That is funny. Generally I never use constants at all.
Kenny Lau (Mar 29 2018 at 17:44):
right, you shouldn't use constant
Nima (Mar 29 2018 at 17:46):
@Kevin Buzzard You mean constant for types, right? Otherwise, how do you assume a relation is given to you?
Kevin Buzzard (Mar 29 2018 at 17:54):
I never ever use constants. I would write something like example (le : X -> X -> Prop) : blah
Kenny Lau (Mar 29 2018 at 17:55):
@Nima you use variable
Kevin Buzzard (Mar 29 2018 at 17:55):
Or variable
yes
Kevin Buzzard (Mar 29 2018 at 17:55):
I would use variables or the trick above for an abstract relation.
Kevin Buzzard (Mar 29 2018 at 17:56):
For a "well-known" one like le
I would use the type class system
Kevin Buzzard (Mar 29 2018 at 17:57):
Like this:
Kevin Buzzard (Mar 29 2018 at 17:58):
variables (X : Type) [has_le X] variables (x y : X) #check has_le.le x y #check x ≤ y
Kevin Buzzard (Mar 29 2018 at 17:58):
Making X an instance of class has_le
enables me to use the notation \leq
Kenny Lau (Mar 29 2018 at 17:58):
\le
:)
Kevin Buzzard (Mar 29 2018 at 17:58):
no way!
Kevin Buzzard (Mar 29 2018 at 17:59):
You'll be telling me I don't have to type \forall
next
Kenny Lau (Mar 29 2018 at 17:59):
I know the shortest code for the symbols I use :P
Kenny Lau (Mar 29 2018 at 17:59):
\fo
Kevin Buzzard (Mar 29 2018 at 18:00):
I discovered that one because I once typed something like \fo4all
followed by a space
Kevin Buzzard (Mar 29 2018 at 18:00):
and was like "wooah, it's magic"
Kevin Buzzard (Mar 29 2018 at 18:00):
"it worked anyway"
Kevin Buzzard (Mar 29 2018 at 18:01):
Back to the point
Kevin Buzzard (Mar 29 2018 at 18:02):
you can write [H : has_le X]
if you want to give the construction a name. Otherwise it ends up being called something like _inst_1
Kenny Lau (Mar 29 2018 at 18:02):
but most of the time you don't want to do that
Kevin Buzzard (Mar 29 2018 at 18:03):
Right, most of the time you want Lean to figure it out for you
Nima (Mar 29 2018 at 18:25):
Thanks,
Would you please answer this one as well?
I know the following example can be solve by replacing the last line with
exact hm
. But why I receive a this error:
Mario Carneiro (Mar 29 2018 at 18:27):
match is not a tactic. If you want to use it you have to say exact match ...
Mario Carneiro (Mar 29 2018 at 18:28):
When you write it there lean gets all sorts of confused in the parsing
Mario Carneiro (Mar 29 2018 at 18:29):
also match
requires end
after it
Nima (Mar 29 2018 at 18:30):
@Mario Carneiro Awesome!
Mario Carneiro (Mar 29 2018 at 18:30):
The tactic equivalent of match
is cases
, but I think you have already discovered this
Mario Carneiro (Mar 29 2018 at 18:31):
so you could use cases hm with m hh,
in place of the match line
Nima (Mar 29 2018 at 19:03):
@Mario Carneiro Thanks a lot, I finally finished a proof! (super simple one, yet 65 lines!!)
Nima (Mar 29 2018 at 20:52):
This is about coercions
The first example in the following code works perfectly fine.
However, the second example cannot be type-checked. I know this direction in general is not possible. But this example, trv α
contains all elements of α
. So how should I prove the second example?
universe u variables {α : Type u} [linear_order α] inductive constraint (α:Type u) | trv : constraint | stt (bnd:α) (low:Prop) : constraint | nst (bnd:α) (low:Prop) : constraint namespace constraint def setof : constraint α → α → Prop := begin intros c a, cases c with bnd low bnd low, case trv {exact true}, case stt bnd low {exact low ∧ bnd<a ∨ ¬ low ∧ a < bnd}, case nst bnd low {exact low ∧ bnd≤a ∨ ¬ low ∧ a ≤ bnd}, end instance constraint_to_sort : has_coe_to_sort (constraint α) := {S := Type u, coe := λ S, {x // setof S x}} example : ∀ (c:constraint α) (a:c), setof c a := begin intros c a, exact @subtype.property α (setof c) a end example : (∀ (a : (trv α)), ff) → ∀ a:α, ff := begin intros h a, exact h a end end constraint
Kenny Lau (Mar 29 2018 at 20:53):
what is the intuition behind all these?
Nima (Mar 29 2018 at 20:54):
Using coercion we can say every natural number is a real number. But what if we know r
is a real number 2
. How we can use it as a natural number.
Kenny Lau (Mar 29 2018 at 20:55):
what is trv stt nst?
Nima (Mar 29 2018 at 20:58):
Constructors of constraint
. The code defines coercion from constraint α
to α
. I am trying to go back from α
to constraint α
. In the second example, trv α
is a trivial constraint that is satisfied by every element of type α
.
Kenny Lau (Mar 29 2018 at 21:00):
btw setof
doesn't type check
Nima (Mar 29 2018 at 21:02):
I copy pasted it from VSCode, it type checks perfectly fine.
Someone else told me if they remove bnd
and low
right after case stt
and case nst
they can compile it as well.
Kenny Lau (Mar 29 2018 at 21:02):
right
Kenny Lau (Mar 29 2018 at 21:02):
universe u variables {α : Type u} [linear_order α] inductive constraint (α:Type u) | trv : constraint | stt (bnd:α) (low:Prop) : constraint | nst (bnd:α) (low:Prop) : constraint namespace constraint def setof : constraint α → α → Prop := begin intros c a, cases c with bnd low bnd low, case trv {exact true}, case stt {exact low ∧ bnd<a ∨ ¬ low ∧ a < bnd}, case nst {exact low ∧ bnd≤a ∨ ¬ low ∧ a ≤ bnd}, end instance constraint_to_sort : has_coe_to_sort (constraint α) := {S := Type u, coe := λ S, {x // setof S x}} example : ∀ (c:constraint α) (a:c), setof c a := begin intros c a, exact @subtype.property α (setof c) a end example : (∀ (a : (trv α)), ff) → ∀ a:α, ff := begin intros h a, exact h ⟨a, trivial⟩ end end constraint
Kenny Lau (Mar 29 2018 at 21:02):
solution ^
Kenny Lau (Mar 29 2018 at 21:03):
also, I would avoid using any tactics in definitions
Nima (Mar 29 2018 at 21:03):
Thanks, so trivial
is basically a proof that a
is satisfied by trv α
. Right?
Nima (Mar 29 2018 at 21:04):
also, I would avoid using any tactics in definitions
"Fresh off the boat", so no idea what best practices are. Just trying to survive ;)
Kenny Lau (Mar 29 2018 at 21:05):
oh, ok, sorry
Kenny Lau (Mar 29 2018 at 21:06):
def setof : constraint α → α → Prop | (trv α) a := true | (stt bnd low) a := low ∧ bnd<a ∨ ¬ low ∧ a < bnd | (nst bnd low) a := low ∧ bnd≤a ∨ ¬ low ∧ a ≤ bnd
Nima (Mar 29 2018 at 21:08):
Cool, thanks
Nima (Mar 29 2018 at 22:33):
Is there any tactic that can push the negation inside quantifiers?
For example ¬ ∀ x,∃ y, p x y
should become ∃ x,∀ y, ¬ p x y
.
Using classical
is fine.
Andrew Ashworth (Mar 29 2018 at 23:12):
no. use a lemma
Nima (Mar 29 2018 at 23:19):
@Andrew Ashworth You mean some lemma (probably two) twice. Right? I was looking for some tactic to push the negation all the way inside. Not sure how difficult it is to write such a tactic. I don't think writing a lemma is the solution, or at least I don't know how that can be done using a lemma such that one invocation of it will do the job.
Andrew Ashworth (Mar 29 2018 at 23:32):
i mean, literally write a lemma stating ¬ ∀ x,∃ y, p x y
iff ∃ x,∀ y, ¬ p x y
. Then rewrite with it.
Nima (Mar 30 2018 at 02:06):
How do I find Lean version that is running online? I am using 3.3.0 (re-downloaded 5 minutes ago) and cannot typecheck the following code from Programming in Lean (the online Lean typechecks it):
import system.io variable [io.interface] open nat io def print_squares : ℕ → io unit | 0 := return () | (succ n) := print_squares n >> put_str (nat.to_string n ++ "^2 = " ++ nat.to_string (n * n) ++ "\n") #eval print_squares 100
Mario Carneiro (Mar 30 2018 at 02:20):
The latest version of lean doesn't use io.interface
. Try removing that line and the rest should work
Nima (Mar 30 2018 at 02:22):
Removing that adds to error. The current error I am receiving is unknown identifier 'nat.to_string'
.
If it is of any help, I am on Mac and downloaded the binary version.
Mario Carneiro (Mar 30 2018 at 03:00):
Looks like it's just to_string
now
Kenny Lau (Mar 30 2018 at 03:01):
what did I just see
Kenny Lau (Mar 30 2018 at 03:01):
is Lean basically python now
Andrew Ashworth (Mar 30 2018 at 03:24):
i think there's room to add more handy programming gadgets once lean 4 rolls around and we can extend the parser
Andrew Ashworth (Mar 30 2018 at 03:26):
map (fun x -> x * x) [0 .. 10] = [0, 1, 4, 9, 16, 25, 36, 49, 56, 64, 81, 100]
would be sweet
Kenny Lau (Mar 30 2018 at 03:26):
we already have list.map
Andrew Ashworth (Mar 30 2018 at 03:26):
you can't write [0 .. 10]
right now
Kenny Lau (Mar 30 2018 at 03:26):
list.range
Andrew Ashworth (Mar 30 2018 at 03:27):
or more generally [0 .. 2 .. 10]
where you can supply a step
Andrew Ashworth (Mar 30 2018 at 03:28):
aha, that's not quite so simple to declare with a notation
command
Kenny Lau (Mar 30 2018 at 03:29):
import system.io open nat io def print_fib_core : ℕ → ℕ × ℕ × io unit | 0 := (0, 1, return ()) | (succ n) := let (a, b, c) := print_fib_core n in (b, a+b, c >> put_str ("fib " ++ to_string n ++ " = " ++ to_string a ++ "\n")) def print_fib : ℕ → io unit := λ n, (print_fib_core n).2.2 #eval print_fib 100
Kenny Lau (Mar 30 2018 at 03:29):
fib 0 = 0 fib 1 = 1 fib 2 = 1 fib 3 = 2 fib 4 = 3 fib 5 = 5 fib 6 = 8 fib 7 = 13 fib 8 = 21 fib 9 = 34 fib 10 = 55 [...] fib 90 = 2880067194370816120 fib 91 = 4660046610375530309 fib 92 = 7540113804746346429 fib 93 = 12200160415121876738 fib 94 = 19740274219868223167 fib 95 = 31940434634990099905 fib 96 = 51680708854858323072 fib 97 = 83621143489848422977 fib 98 = 135301852344706746049 fib 99 = 218922995834555169026
Kenny Lau (Mar 30 2018 at 03:30):
cool, now I can do Project Euler with it
Kenny Lau (Mar 30 2018 at 03:30):
this is ridiculous
Andrew Ashworth (Mar 30 2018 at 03:31):
project euler in a functional language is painful, but that's probably because i don't speak monad
Kenny Lau (Mar 30 2018 at 03:32):
you only need io unit
:P
Nima (Mar 30 2018 at 03:33):
@Kenny Lau Thank you, it worked (I still need variable [io.interface]
, but it works)
Kenny Lau (Mar 30 2018 at 03:34):
ok
Mario Carneiro (Mar 30 2018 at 03:51):
I get VM does not have code for 'unsafe_monad_from_pure_bind'
when I try to run that code
Kenny Lau (Mar 30 2018 at 03:51):
which code?
Mario Carneiro (Mar 30 2018 at 03:53):
your fib code
Kenny Lau (Mar 30 2018 at 03:53):
what is your lean version?
Kenny Lau (Mar 30 2018 at 03:53):
Lean (version 3.3.1, commit 28f4143be31b, RELEASE)
Mario Carneiro (Mar 30 2018 at 03:53):
28f414
Kenny Lau (Mar 30 2018 at 03:53):
wow
Kenny Lau (Mar 30 2018 at 03:54):
what does that mean
Mario Carneiro (Mar 30 2018 at 03:54):
same as yours, evidently
Mario Carneiro (Mar 30 2018 at 03:56):
I can't even findunsafe_monad_from_pure_bind
in the lean repo, it's a mystery where that comes from
Mario Carneiro (Mar 30 2018 at 03:56):
meh, restart and it's fine
Kenny Lau (Mar 30 2018 at 03:57):
what is the intended use for bit0 and bit1?
Kenny Lau (Mar 30 2018 at 03:57):
can I use them in recursion?
Mario Carneiro (Mar 30 2018 at 04:05):
Here's my version of the fib printer:
def fib_core : ℕ → ℕ × ℕ | 0 := (0, 1) | (succ n) := let (a, b) := fib_core n in (b, a+b) def fib (n) := (fib_core n).1 def print_fib (n : ℕ) : io unit := (list.range 100).mmap' (λ n, put_str ("fib " ++ to_string n ++ " = " ++ to_string (fib n) ++ "\n")) #eval print_fib 100
It's asymptotically slower than Kenny's version since it recomputes fib
rather than calculating as it goes, but this is a bit closer to what you would expect in another programming language without any fancy tricks
Mario Carneiro (Mar 30 2018 at 04:06):
bit0
and bit1
are used by the parser to construct natural number literals and other number literals like 7
, which is actually bit1 (bit1 (bit1 1)))
in whatever type (it must have typeclasses for has_add
and has_one
)
Mario Carneiro (Mar 30 2018 at 04:07):
If you want to define a function by binary recursion over nat
, use nat.binary_rec_on
, which uses bit b n
which generalizes bit0
and bit1
with a boolean bit parameter
Kenny Lau (Mar 30 2018 at 04:09):
def list.update {α : Type*} : list α → list nat → α → list α | L [] x := L | L (h::t) x := (L.update t x).update_nth h x namespace eratosthenes def aux1 : nat → nat → nat | sq lo := if (lo+1)*(lo+1) > sq then lo else lo+1 def isqrt : nat → nat | n := if H : n / 4 < n then (aux1 n (isqrt (n / 4) * 2)) else n def aux2 : list bool → nat → nat → list bool := λ L len n, L.update (list.map (λ z, (z + n) * n) (list.range $ len / n)) ff def aux3 : list bool → nat → nat → list bool | L len 0 := L | L len 1 := L | L len (n+1) := aux2 (aux3 L len n) len (n+1) def aux4 : nat → list bool := λ n, aux3 (list.repeat tt n) n (isqrt n) def aux5 : list bool → nat → list nat := λ L n, (list.range n).filter $ λ z, L.nth z = tt end eratosthenes def eratosthenes : nat → list nat := λ n, (eratosthenes.aux5 (eratosthenes.aux4 n) n).drop 2 #eval eratosthenes 1000
Kenny Lau (Mar 30 2018 at 04:09):
why is this so slow
Mario Carneiro (Mar 30 2018 at 04:10):
geez, name your stuff better
Kenny Lau (Mar 30 2018 at 04:11):
well I don't intend to PR it :P
Mario Carneiro (Mar 30 2018 at 04:11):
it's like reading decompiled sources
Kenny Lau (Mar 30 2018 at 04:11):
oh, sorry
Kenny Lau (Mar 30 2018 at 04:11):
aux1 is for isqrt
Andrew Ashworth (Mar 30 2018 at 04:11):
heh, sieve of e
Kenny Lau (Mar 30 2018 at 04:11):
aux2 does one sieve
Andrew Ashworth (Mar 30 2018 at 04:11):
https://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf
Mario Carneiro (Mar 30 2018 at 04:11):
What's the anticipated order?
Kenny Lau (Mar 30 2018 at 04:12):
of what?
Mario Carneiro (Mar 30 2018 at 04:12):
of the code
Kenny Lau (Mar 30 2018 at 04:12):
it sieves sqrt(n) times
Kenny Lau (Mar 30 2018 at 04:12):
each time taking n
Kenny Lau (Mar 30 2018 at 04:12):
I think it's O(n^1.5) then
Mario Carneiro (Mar 30 2018 at 04:12):
I see several quadratic passes at least
Kenny Lau (Mar 30 2018 at 04:12):
where is the quadratic pass
Mario Carneiro (Mar 30 2018 at 04:13):
(list.range n).filter $ λ z, L.nth z = tt
passes over L
for each n
Kenny Lau (Mar 30 2018 at 04:13):
oh, how should I fix that
Kenny Lau (Mar 30 2018 at 04:13):
ah, I should deconstruct the list
Andrew Ashworth (Mar 30 2018 at 04:13):
paper i linked discusses the sieve in a functional setting exhaustively
Mario Carneiro (Mar 30 2018 at 04:14):
You should put comments or something, it's not obvious what the auxes do
Kenny Lau (Mar 30 2018 at 04:15):
should I implement the code form your paper?
Andrew Ashworth (Mar 30 2018 at 04:16):
i mean, if you want a functional Sieve of Eratosthenes , that is also pretty fast, yes
Kenny Lau (Mar 30 2018 at 04:17):
does it use data structures that I don't have?
Kenny Lau (Mar 30 2018 at 04:17):
PriorityQ?
Andrew Ashworth (Mar 30 2018 at 04:19):
this is getting a bit out of hand, but you could implement one from okasaki's "purely functional data structures"
Mario Carneiro (Mar 30 2018 at 04:19):
Yeah, we don't have that, maybe rb_map
will work
Mario Carneiro (Mar 30 2018 at 04:19):
Beware, Okasaki assumes a susp
type (aka memoization), but we don't have one
Andrew Ashworth (Mar 30 2018 at 04:20):
hm. then i'd use the red-black tree in lean core for everything
Mario Carneiro (Mar 30 2018 at 04:21):
You actually could make your original code a lot faster simply by using arrays instead of lists. You are using them like arrays anyway
Mario Carneiro (Mar 30 2018 at 04:21):
and that will make L.nth
asymptotically fast
Kenny Lau (Mar 30 2018 at 04:22):
I've never used array :P
Kenny Lau (Mar 30 2018 at 04:22):
I don't even know arrays exist
Mario Carneiro (Mar 30 2018 at 04:23):
array
is like vector
, it's a list with a fixed (in the type) size
Mario Carneiro (Mar 30 2018 at 04:24):
and importantly, it's implemented as an C++ array, meaning that updates and nth are fast
Kenny Lau (Mar 30 2018 at 04:27):
can I trace the function calls? i.e. debugging?
Andrew Ashworth (Mar 30 2018 at 04:27):
is vector a dynamically growing array under the hood?
Andrew Ashworth (Mar 30 2018 at 04:29):
... i wonder how much work it would take to use a dynamically growing array for lean's list impl
Mario Carneiro (Mar 30 2018 at 04:32):
vector
is literally just a subtype of list
. array
is isomorphic to vector
, and is implemented as the parray
type in C++, which is a C++ array with some added support for end extension and persistent usage
Andrew Ashworth (Mar 30 2018 at 04:36):
ahh, i see
Kenny Lau (Mar 30 2018 at 04:42):
def list.update {α : Type*} : list α → list nat → α → list α | L [] x := L | L (h::t) x := (L.update t x).update_nth h x def list.extract {α : Type*} [decidable_eq α] : list α → nat → α → list nat | L 0 x := [] | [] (n+1) x := [] | (h::t) (n+1) x := if h = x then n :: t.extract n x else t.extract n x namespace eratosthenes def aux1 : nat → nat → nat | sq lo := if (lo+1)*(lo+1) > sq then lo else lo+1 def isqrt : nat → nat | n := if H : n / 4 < n then aux1 n (isqrt (n / 4) * 2) else n -- from L, make every n-th item false def step : list bool → nat → nat → list bool := λ L len n, L.update (list.map (λ z, (z + n) * n) (list.range $ len / n)) ff -- do "step" for every integer from 2 to n def sieve : list bool → nat → nat → list bool | L len 0 := L | L len 1 := L | L len (n+1) := if L.nth (n+1) = tt then step (sieve L len n) len (n+1) else sieve L len n -- invoke sieve with sqrt(n) def prime.bool : nat → list bool := λ n, sieve (list.repeat tt n) n (isqrt n) end eratosthenes def eratosthenes : nat → list nat := λ n, ((eratosthenes.prime.bool n).reverse.extract n tt).reverse.drop 2 #eval eratosthenes 1000
Kenny Lau (Mar 30 2018 at 04:42):
@Mario Carneiro what do you think is the complexity of this?
Kenny Lau (Mar 30 2018 at 04:44):
ah, I see a quadratic pass
Mario Carneiro (Mar 30 2018 at 04:44):
L.update
is quadratic
Kenny Lau (Mar 30 2018 at 04:44):
right
Mario Carneiro (Mar 30 2018 at 04:45):
I think you should define step
by recursion
Mario Carneiro (Mar 30 2018 at 04:45):
it shouldn't be hard to make every n
th item false by keeping an accumulator
Mario Carneiro (Mar 30 2018 at 04:46):
You can't do step
in less than O(n) time with this data structure, so sieve
is necessarily O(n^2)
Kenny Lau (Mar 30 2018 at 04:47):
what do you mean?
Mario Carneiro (Mar 30 2018 at 04:47):
step
has to walk down the entire list to change stuff
Kenny Lau (Mar 30 2018 at 04:47):
but I only need to call step sqrt(n)
times
Mario Carneiro (Mar 30 2018 at 04:48):
Ah, that's true. So n^1.5 seems likely
Mario Carneiro (Mar 30 2018 at 04:49):
As that paper will tell you though, this isn't "true" eratosthenes since you have to visit all the skipped entries multiple times
Kenny Lau (Mar 30 2018 at 04:49):
I see
Kenny Lau (Mar 30 2018 at 04:50):
well that paper is too technical
Kenny Lau (Mar 30 2018 at 04:52):
it's O(n^1.5) now, empirically
Kenny Lau (Mar 30 2018 at 04:52):
because I can do 10000 now
Kenny Lau (Mar 30 2018 at 04:52):
(how pathetic)
Kenny Lau (Mar 30 2018 at 04:52):
def list.extract {α : Type*} [decidable_eq α] : list α → nat → α → list nat | L 0 x := [] | [] (n+1) x := [] | (h::t) (n+1) x := if h = x then n :: t.extract n x else t.extract n x namespace eratosthenes def aux1 : nat → nat → nat | sq lo := if (lo+1)*(lo+1) > sq then lo else lo+1 def isqrt : nat → nat | n := if H : n / 4 < n then aux1 n (isqrt (n / 4) * 2) else n -- from L, make every n-th item false def step : list bool → nat → nat → list bool := λ L n start, L.enum.map (λ z, if z.1 % n = 0 ∧ start ≤ z.1 then ff else z.2) -- do "step" for every integer from 2 to n def sieve : list bool → nat →list bool | L 0 := L | L 1 := L | L (n+1) := if L.nth (n+1) = tt then step (sieve L n) (n+1) ((n+1)*(n+1)) else sieve L n -- invoke sieve with sqrt(n) def prime.bool : nat → list bool := λ n, sieve (list.repeat tt n) (isqrt n) end eratosthenes def eratosthenes : nat → list nat := λ n, ((eratosthenes.prime.bool n).reverse.extract n tt).reverse.drop 2 #eval eratosthenes 10000
Kenny Lau (Mar 30 2018 at 04:52):
now I'm going to redo it
Kenny Lau (Mar 30 2018 at 04:53):
i.e. use list nat
instead of list bool
Kenny Lau (Mar 30 2018 at 04:53):
and use accumulator instead of mod
Andrew Ashworth (Mar 30 2018 at 04:53):
well, that paper has a list based implementation on page 11
Kenny Lau (Mar 30 2018 at 04:54):
oh
Andrew Ashworth (Mar 30 2018 at 04:54):
but, you'll need a lazy sequence type
Kenny Lau (Mar 30 2018 at 04:54):
coinduction is coming soon, I hope :)
Andrew Ashworth (Mar 30 2018 at 04:54):
i don't know if lean supports lazy semantics right now
Kenny Lau (Mar 30 2018 at 04:54):
we already have stream
Kenny Lau (Mar 30 2018 at 04:54):
but I don't know how computable it is
Mario Carneiro (Mar 30 2018 at 04:55):
Seriously, use array
Mario Carneiro (Mar 30 2018 at 04:55):
if you care about speed, use array
Mario Carneiro (Mar 30 2018 at 04:55):
linked lists are known horrible in almost all cases in CS theory
Mario Carneiro (Mar 30 2018 at 04:56):
stream
exists and is computable, but is not remotely efficient as a stream
Mario Carneiro (Mar 30 2018 at 04:57):
the hard part about making that paper applicable in lean is all the tricks used to take advantage of laziness
Mario Carneiro (Mar 30 2018 at 04:58):
AFAIK there are no plans to make lean lazy. You can manually add laziness using thunk
Andrew Ashworth (Mar 30 2018 at 04:58):
yeah, it's unfortunate that the most popular pure functional language emphasizes laziness so much
Andrew Ashworth (Mar 30 2018 at 04:59):
it's really hard to move code snippets and ideas over
Mario Carneiro (Mar 30 2018 at 04:59):
ML is a good functional strict language, but I find it often does too much impure stuff
Andrew Ashworth (Mar 30 2018 at 04:59):
that's a benefit :)
Andrew Ashworth (Mar 30 2018 at 04:59):
(i use f# when i can)
Mario Carneiro (Mar 30 2018 at 05:00):
In the sense that it makes it difficult to transfer ideas over
Andrew Ashworth (Mar 30 2018 at 05:00):
i haven't tried to do much imperative programming in lean
Andrew Ashworth (Mar 30 2018 at 05:00):
is it hard to emulate?
Kenny Lau (Mar 30 2018 at 05:06):
now it's much faster!
Kenny Lau (Mar 30 2018 at 05:06):
namespace eratosthenes def aux1 : nat → nat → nat | sq lo := if (lo+1)*(lo+1) > sq then lo else lo+1 def isqrt : nat → nat | n := if H : n / 4 < n then aux1 n (isqrt (n / 4) * 2) else n -- from L, remove every item divisible by n def step : list nat → nat → list nat | [] n := [] | (h::t) n := if h%n = 0 then step t n else h::step t n -- each time : remove one element, do step on that element def sieve : nat → list nat → list nat | hi [] := [] | hi (h::t) := if h ≤ hi then (h::step t h) else (h::t) end eratosthenes def eratosthenes : nat → list nat := λ n, eratosthenes.sieve (eratosthenes.isqrt n) ((list.range n).drop 2) #eval eratosthenes 10000
Mario Carneiro (Mar 30 2018 at 05:13):
I think step
can be expressed as a list.filter
without a performance hit with this approach
Mario Carneiro (Mar 30 2018 at 05:13):
Isn't sieve
recursive?
Kenny Lau (Mar 30 2018 at 05:14):
yes, I just discovered the bug xd
Kenny Lau (Mar 30 2018 at 05:15):
now Lean doesn't trust that my recursion is well-founded
Kenny Lau (Mar 30 2018 at 05:15):
I can't be bothered to prove that to Lean
Kenny Lau (Mar 30 2018 at 05:15):
so, see you :P
Kenny Lau (Mar 30 2018 at 05:28):
namespace eratosthenes -- O(1) def aux1 : nat → nat → nat | sq lo := if (lo+1)*(lo+1) > sq then lo else lo+1 -- O(log n) def isqrt : nat → nat | n := if H : n / 4 < n then aux1 n (isqrt (n / 4) * 2) else n -- O(n) -- from L, remove every item divisible by n def step : list nat → nat → list nat | [] n := [] | (h::t) n := if h%n = 0 then step t n else h::step t n theorem aux (t : list nat) (h : nat) : list.sizeof (step t h) ≤ list.sizeof t := begin induction t with h1 t1 ih, { dsimp [step], refl }, { dsimp [step], by_cases H : h1 % h = 0, { simp [H, list.sizeof], apply le_trans ih, apply le_trans (nat.le_add_right _ (sizeof h1)), exact nat.le_add_left _ _ }, { simp [H, list.sizeof], apply nat.add_le_add_left, rw add_comm, apply nat.add_le_add_right, exact ih } } end theorem aux2 (t : list nat) (h : nat) : has_well_founded.r (step t h) (h :: t) := begin dsimp [has_well_founded.r, sizeof_measure, measure, inv_image, sizeof, has_sizeof.sizeof, list.sizeof], apply lt_of_le_of_lt (aux t h), apply nat.lt_add_of_pos_left, rw add_comm, exact nat.zero_lt_succ _ end -- O(n^1.5) -- each time : remove one element, do step on that element def sieve (hi : nat) : list nat → list nat | [] := [] | (h::t) := if h ≤ hi then (h::(sieve $ step t h)) else (h::t) using_well_founded { dec_tac := `[exact aux2 t h] } end eratosthenes -- O(n^1.5) def eratosthenes : nat → list nat := λ n, eratosthenes.sieve (eratosthenes.isqrt n) ((list.range n).drop 2) #eval eratosthenes 10000
Kenny Lau (Mar 30 2018 at 05:29):
@Mario Carneiro I lied
Mario Carneiro (Mar 30 2018 at 05:30):
If you used filter
for step
, the lemma would be easier
Kenny Lau (Mar 30 2018 at 05:49):
btw it can handle 100000
now
Kenny Lau (Mar 30 2018 at 05:52):
@Mario Carneiro I don't see what difference it makes. It isn't a lemma that L.filter p has size at most L
Mario Carneiro (Mar 30 2018 at 05:53):
It's a sublist from filter_sublist
, and sublist implies length less equal
Kenny Lau (Mar 30 2018 at 05:53):
so how exactly would I prove its well-foundedness?
Mario Carneiro (Mar 30 2018 at 06:24):
def step (L : list nat) (n : nat) : list nat := L.filter (λ a, a%n ≠ 0) def sieve (hi : nat) : list nat → list nat | [] := [] | (h::t) := have (step t h).length < (h :: t).length, from lt_succ_of_le $ list.length_le_of_sublist $ list.filter_sublist _, if h ≤ hi then h::sieve (step t h) else h::t using_well_founded { rel_tac := λ _ _, `[exact ⟨_, measure_wf list.length⟩], dec_tac := `[exact this] }
Kenny Lau (Mar 30 2018 at 06:25):
oh, changing the relation
Kenny Lau (Mar 30 2018 at 06:25):
I see
Nima (Mar 31 2018 at 00:45):
The following is from Programming in Lean and works perfectly fine in the online Lean. Any idea how I should make it also work in version 3.3.0?
open expr tactic classical meta def normalize_hyp (lemmas : list expr) (hyp : expr) : tactic unit := do try (simp_at hyp lemmas)
Simon Hudon (Mar 31 2018 at 00:47):
what errors are you encountering?
Kenny Lau (Mar 31 2018 at 00:47):
sandbox3.lean:6:3: error don't know how to synthesize placeholder context: lemmas : list expr, hyp : expr, normalize_hyp : tactic unit ⊢ Type ? sandbox3.lean:6:8: error unknown identifier 'simp_at'
Nima (Mar 31 2018 at 00:50):
Yes, those errors
Simon Hudon (Mar 31 2018 at 00:53):
Can you try simp_hyp
instead of simp_at
?
Kenny Lau (Mar 31 2018 at 00:53):
type mismatch at application simp_hyp hyp term hyp has type expr but is expected to have type simp_lemmas
Simon Hudon (Mar 31 2018 at 01:02):
ok, first, swap the order of the arguments: simp_hyp lemmas hyp
Simon Hudon (Mar 31 2018 at 01:02):
second, let's construct a simp_lemmas
Simon Hudon (Mar 31 2018 at 01:04):
meta def normalize_hyp (lemmas : list expr) (hyp : expr) : tactic unit := do s <- simp_lemmas.append simp_lemmas.mk lemmas, try (simp_at s hyp)
Nima (Mar 31 2018 at 01:08):
simp_at
(unknown identifier
)?
If I use simp_hyp
give me the same error
type mismatch at application simp_hyp hyp term hyp has type expr but is expected to have type simp_lemmas Additional information: /Users/nima/Dropbox/Codes/Lean/Interval/tmp.lean:5:8: context: switched to simple application elaboration procedure because failed to use expected type to elaborate it, error message type mismatch, term (simp_hyp ?m_1 ?m_2 h {max_steps := simp.default_max_steps, contextual := ff, lift_eq := tt, canonize_instances := tt, canonize_proofs := ff, use_axioms := tt, zeta := tt, beta := tt, eta := tt, proj := tt, iota := tt, single_pass := ff, fail_if_unchanged := tt, memoize := tt}) has type expr → tactic expr : Type but is expected to have type tactic ?m_1 : Type ?
Simon Hudon (Mar 31 2018 at 01:10):
Sorry, the second version was wrong. I fixed it
Nima (Mar 31 2018 at 01:16):
Is there any lean file that I can download from github to have the fix?
Simon Hudon (Mar 31 2018 at 01:17):
I mean, this is what it should be:
meta def normalize_hyp (lemmas : list expr) (hyp : expr) : tactic unit := do s <- simp_lemmas.append simp_lemmas.mk lemmas, try (simp_at s hyp)
Nima (Mar 31 2018 at 01:19):
but simp_at
gives me unknown identifier 'simp_at'
error
Simon Hudon (Mar 31 2018 at 01:20):
Sorry I keep making the same typo:
meta def normalize_hyp (lemmas : list expr) (hyp : expr) : tactic unit := do s <- simp_lemmas.append simp_lemmas.mk lemmas, try (simp_hyp s hyp)
Nima (Mar 31 2018 at 01:22):
This gives me the following error:
type mismatch at application simp_hyp s hyp term hyp has type expr but is expected to have type opt_param (list name) list.nil Additional information: /Users/nima/Dropbox/Codes/Lean/Interval/tmp.lean:5:8: context: switched to simple application elaboration procedure because failed to use expected type to elaborate it, error message type mismatch, term (simp_hyp ?m_1 ?m_2 h {max_steps := simp.default_max_steps, contextual := ff, lift_eq := tt, canonize_instances := tt, canonize_proofs := ff, use_axioms := tt, zeta := tt, beta := tt, eta := tt, proj := tt, iota := tt, single_pass := ff, fail_if_unchanged := tt, memoize := tt}) has type expr → tactic expr : Type but is expected to have type tactic ?m_1 : Type ?
Simon Hudon (Mar 31 2018 at 01:23):
that's progress
Simon Hudon (Mar 31 2018 at 01:24):
How about
meta def normalize_hyp (lemmas : list expr) (hyp : expr) : tactic unit := do s <- simp_lemmas.append simp_lemmas.mk lemmas, try (simp_hyp s [] hyp)
Nima (Mar 31 2018 at 01:26):
Wow! Thanks
Simon Hudon (Mar 31 2018 at 01:27):
No problems. Sorry for the excessive back and forth.
Mario Carneiro (Mar 31 2018 at 01:28):
I don't see simp_at
in the lean web editor either. Is it in 3.3.0?
Nima (Mar 31 2018 at 01:31):
No problem at all.
I don't know how to check Lean version, mine is supposed to be 3.3.0.
The same chapter gives me error.
Last line unknown identifier 'monad.for''
Is there a magic fix for this one as well?
The for' tactic, like the for tactic, applies the second argument to each element of the first, but it returns unit rather than accumulate the results in a list.
open expr tactic classical monad meta def normalize_hyp (lemmas : list expr) (hyp : expr) : tactic unit := do s <- simp_lemmas.append simp_lemmas.mk lemmas, try (simp_hyp s [] hyp) meta def normalize_hyps : tactic unit := do hyps ← local_context, lemmas ← monad.mapm mk_const [``not_not_intro], monad.for' hyps (normalize_hyp lemmas)
Simon Hudon (Mar 31 2018 at 01:32):
I don't see
simp_at
in the lean web editor either. Is it in 3.3.0?
It disappeared in https://github.com/leanprover/lean/commit/69ed291aab8493a7fb33b52dc2982e2db417761f
in July
Simon Hudon (Mar 31 2018 at 01:32):
@Nima you can check your version with lean --version
Simon Hudon (Mar 31 2018 at 01:34):
Instead of monad.for' hyps (normalize_hyp lemmas)
try hyps.mmap' (normalize_hyp lemmas)
Nima (Mar 31 2018 at 01:36):
Fantastic!
Thanks a lot,
Also, my version is Lean (version 3.3.0, commit fa9c868ed2bb, Release)
Simon Hudon (Mar 31 2018 at 01:39):
Yeah, that's exactly 3.3.0
Simon Hudon (Mar 31 2018 at 01:40):
Programming in Lean seems to be working with a pretty old version of Lean
Nima (Mar 31 2018 at 04:36):
Sorry if this is too stupid, but why the following does not type-checks (I don't care the example itself) :
universe u variable {α : Type u} example (p:α → Prop) : (¬(∃ (a:α), ¬ p a)) → (∀ (a:α), p a) := begin intros h a, have h' : (p a) → p a, from admit, admit end
I get the following error at have
type mismatch at application p a term a has type p a : Prop but is expected to have type α : Type u state: α : Type u, p : α → Prop, h : ¬∃ (a : α), ¬p a, a : α ⊢ p a
This is Tactic State right before have
α : Type u, p : α → Prop, h : ¬∃ (a : α), ¬p a, a : α ⊢ p a
Simon Hudon (Mar 31 2018 at 04:39):
That's puzzling
Mario Carneiro (Mar 31 2018 at 04:41):
You can't from admit
, admit
is a tactic not a term. Use from sorry
or just admit
Simon Hudon (Mar 31 2018 at 04:42):
Dang! I completely missed that!
Mario Carneiro (Mar 31 2018 at 04:42):
But the error is actually an issue with the variable a
together with p -> q
instead of \forall (_ : p), q
Nima (Mar 31 2018 at 04:43):
So the following change gives me the same error:
have h' : (p a) → (p a), admit, admit
Mario Carneiro (Mar 31 2018 at 04:44):
https://github.com/leanprover/lean/issues/1822
Simon Hudon (Mar 31 2018 at 04:44):
Oooooooh! I had to stare a it for a while. I almost bore a whole through my monitor
Mario Carneiro (Mar 31 2018 at 04:45):
You can work around the issue by writing ∀_:p a, p a
instead of p a -> p a
Mario Carneiro (Mar 31 2018 at 04:45):
or use a variable other than a
Simon Hudon (Mar 31 2018 at 04:46):
Is there any plan to change that horrible convention to name that bound variable a
?
Mario Carneiro (Mar 31 2018 at 04:46):
I think? There are two separate autonaming approaches, one produces a_n
and the other produces _x_n
Mario Carneiro (Mar 31 2018 at 04:47):
The underscore trick switches to the other naming convention
Mario Carneiro (Mar 31 2018 at 04:47):
But it's never as simple as it seems for this stuff. See the issue
Mario Carneiro (Mar 31 2018 at 04:48):
also https://github.com/leanprover/lean/pull/1844
Simon Hudon (Mar 31 2018 at 04:49):
Yeah, don't I know it
Mario Carneiro (Mar 31 2018 at 04:49):
Looks like Leo wanted to postpone a fix because of the new parser (sigh...)
Simon Hudon (Mar 31 2018 at 04:49):
Thanks for the link
Simon Hudon (Mar 31 2018 at 04:50):
I've heard that song before :D
Simon Hudon (Mar 31 2018 at 04:50):
Here's hoping that the new parser also cures cancer :D
Mario Carneiro (Mar 31 2018 at 04:51):
but don't bother with any medicine or chemotherapy in the meantime
Kenny Lau (Mar 31 2018 at 04:51):
oh, what does double colon mean? as in, {x : : p a}
something like that
Mario Carneiro (Mar 31 2018 at 04:51):
that's malformed
Kenny Lau (Mar 31 2018 at 04:52):
https://github.com/leanprover/mathlib/pull/88#discussion_r178095763
Kenny Lau (Mar 31 2018 at 04:52):
what about
def next.fixed_point {x : : α} (H : x ≤ f x) : fixed_point
(in similar for previous) then you can shorten some proofs below.
Mario Carneiro (Mar 31 2018 at 04:52):
looks like a typo
Kenny Lau (Mar 31 2018 at 04:52):
oh
Nima (Mar 31 2018 at 04:58):
But the error is actually an issue with the ...
@Mario Carneiro I only understood that part, but it worked. So thank you :)
Simon Hudon (Mar 31 2018 at 05:01):
Basically, have h' : (p a) → p a,
is the same as have h' : ∀ a : p a, p a,
so the first a
has type α
and the second one has type p a
Nima (Mar 31 2018 at 05:28):
Thanks for the clarification,
Some of the discussion on those links were about how Lean should choose name of the bounded variable in ∀ a : p ...
. Right? (I mean is it always a
or starts with a
, so if I don't start names with a
then I will never see this problem again?)
Mario Carneiro (Mar 31 2018 at 05:33):
Yes. Ideally it should be chosen to be distinct from the variables on the RHS, but for some reason this is complicated, so it just uses a name generator and you the user are responsible for not choosing the same name. You should beware of naming your variables a
, a_1
, a_2
etc. as well as _x
, _x_1
, _x_2
etc. Also _inst
and _match
and other underscore names are taken, so you should avoid starting your variables with an underscore.
Kenny Lau (Mar 31 2018 at 07:56):
has type α → α → Prop : Type u but is expected to have type α → α → Type ? : Type (max u (?+1))
Kenny Lau (Mar 31 2018 at 07:56):
is Prop not a type?
Kenny Lau (Mar 31 2018 at 07:57):
btw plift
seems to have resolved the error
Mario Carneiro (Mar 31 2018 at 07:59):
Prop is a Type, but Prop is not Type
Kenny Lau (Mar 31 2018 at 07:59):
Prop is Type 0, no?
Mario Carneiro (Mar 31 2018 at 07:59):
Prop is Sort 0, Type u is Sort (u+1)
Kenny Lau (Mar 31 2018 at 07:59):
oh
Kenny Lau (Mar 31 2018 at 08:00):
don't know how to synthesize placeholder context: α : Type u, _inst_1 : partial_order α, x y : α, hxy : plift (x ≤ y) ⊢ {down := le_trans (hxy.down) ({down := le_refl y}.down)} = hxy
Kenny Lau (Mar 31 2018 at 08:00):
proof irrelevance after plift...?
Mario Carneiro (Mar 31 2018 at 08:01):
you have to case on hxy
first
Mario Carneiro (Mar 31 2018 at 08:02):
or rewrite with plift.up_down
Kenny Lau (Mar 31 2018 at 08:03):
congr_arg plift.up rfl
works
Kenny Lau (Mar 31 2018 at 08:03):
after casing
Kenny Lau (Mar 31 2018 at 08:03):
λ x y ⟨hxy⟩, congr_arg plift.up rfl
Mario Carneiro (Mar 31 2018 at 08:03):
but rfl
doesn't?
Kenny Lau (Mar 31 2018 at 08:04):
I'm stupid
Kenny Lau (Mar 31 2018 at 08:04):
it does
Kenny Lau (Mar 31 2018 at 08:04):
interesting
Kenny Lau (Mar 31 2018 at 08:04):
it goes assertion error without the casing
Kenny Lau (Mar 31 2018 at 08:04):
but I've been getting too many assertion errors I decided to stop caring
Mario Carneiro (Mar 31 2018 at 08:05):
hm, that's not a place I've seen an assertion error before
Kenny Lau (Mar 31 2018 at 08:09):
is there polymorphic empty type?
Kenny Lau (Mar 31 2018 at 08:11):
def empty : category empty := { Mor := empty.rec _, Comp := empty.rec _, Id := empty.rec _, Hid_left := empty.rec _, Hid_right := empty.rec _, Hassoc := empty.rec _ }
Kenny Lau (Mar 31 2018 at 08:11):
lol
Mario Carneiro (Mar 31 2018 at 08:12):
Surprisingly no. You can use ulift empty
in a pinch
Kenny Lau (Mar 31 2018 at 08:13):
and yes, I'm doing category theory again
Kenny Lau (Mar 31 2018 at 08:15):
λ x y z, unit.cases_on z rfl
Kenny Lau (Mar 31 2018 at 08:15):
is there a way to case on z
in the lambda part?
Kenny Lau (Mar 31 2018 at 08:16):
def one : category unit := { Mor := λ _ _, unit, Comp := λ _ _ _ _ _, unit.star, Id := λ _, unit.star, Hid_left := λ _ _, unit.rec rfl, Hid_right := λ _ _, unit.rec rfl, Hassoc := λ _ _ _ _ _ _ _, rfl }
Mario Carneiro (Mar 31 2018 at 08:18):
you can write ()
in the lambda I think, or ⟨⟩
Kenny Lau (Mar 31 2018 at 08:19):
the latter works
Kenny Lau (Mar 31 2018 at 08:22):
def of_monoid (α : Type u) [monoid α] : category unit := { Mor := λ _ _, α, Comp := λ _ _ _, (*), Id := λ _, 1, Hid_left := λ _ _, one_mul, Hid_right := λ _ _, mul_one, Hassoc := λ _ _ _ _, mul_assoc }
Kenny Lau (Mar 31 2018 at 08:22):
how beautifully the structures are compatible
Kenny Lau (Mar 31 2018 at 08:25):
def of_monoid (α : Type u) [monoid α] : category unit := { Mor := λ _ _, α, Comp := λ _ _ _, (*), Id := λ _, 1, Hid_left := λ _ _, one_mul, Hid_right := λ _ _, mul_one, Hassoc := λ _ _ _ _, mul_assoc } def to_monoid (C: category unit) : monoid (C.Mor () ()) := { mul := C.Comp _ _ _, mul_assoc := C.Hassoc () () () (), one := C.Id (), one_mul := C.Hid_left () (), mul_one := C.Hid_right () () }
Kenny Lau (Mar 31 2018 at 08:26):
this is very beautiful
Mario Carneiro (Mar 31 2018 at 08:27):
I think you can do better in that last one... in any category, the homs from an object to itself forms a monoid
Kenny Lau (Mar 31 2018 at 08:29):
def to_monoid {α : Type u} (C: category α) (x : α) : monoid (C.Mor x x) := { mul := C.Comp _ _ _, mul_assoc := C.Hassoc x x x x, one := C.Id x, one_mul := C.Hid_left x x, mul_one := C.Hid_right x x }
Kenny Lau (Mar 31 2018 at 08:29):
done
Kenny Lau (Mar 31 2018 at 08:30):
I just realized the forgetful functor and its adjoint exists in Cat
Kenny Lau (Mar 31 2018 at 08:32):
now one
is a special case of discrete
Kenny Lau (Mar 31 2018 at 08:32):
def discrete (α : Type u) : category α := { Mor := λ _ _, unit, Comp := λ _ _ _ _ _, (), Id := λ _, (), Hid_left := λ _ _ ⟨⟩, rfl, Hid_right := λ _ _ ⟨⟩, rfl, Hassoc := λ _ _ _ _ _ _ _, rfl } def one : category unit := discrete unit
Kenny Lau (Mar 31 2018 at 08:32):
ok technically zero also, but...
Kenny Lau (Mar 31 2018 at 08:33):
def discrete (α : Type u) : category α := { Mor := λ _ _, unit, Comp := λ _ _ _ _ _, (), Id := λ _, (), Hid_left := λ _ _ ⟨⟩, rfl, Hid_right := λ _ _ ⟨⟩, rfl, Hassoc := λ _ _ _ _ _ _ _, rfl } def zero : category empty := discrete empty def one : category unit := discrete unit
Kenny Lau (Mar 31 2018 at 08:39):
@Mario Carneiro how would you call the category * -> *
and the category * => *
?
Kenny Lau (Mar 31 2018 at 08:39):
(one arrow, two arrows)
Mario Carneiro (Mar 31 2018 at 08:39):
what do they mean?
Kenny Lau (Mar 31 2018 at 08:39):
the former is the category with 2 objects and 3 morphisms
Mario Carneiro (Mar 31 2018 at 08:40):
that's two
Kenny Lau (Mar 31 2018 at 08:40):
and the latter is with 4 morphisms
Mario Carneiro (Mar 31 2018 at 08:40):
two'
? two_eq
(since it's the equalizer diagram)
Kenny Lau (Mar 31 2018 at 08:41):
so it doesn't have a name in convention?
Mario Carneiro (Mar 31 2018 at 08:41):
I've never seen it have an official name
Kenny Lau (Mar 31 2018 at 08:41):
yes, I meant official. words.
Kenny Lau (Mar 31 2018 at 08:42):
maybe equalizer_diagram
lol
Mario Carneiro (Mar 31 2018 at 08:43):
Alternatively two_mor
since it's the canonical two morphism diagram
Kenny Lau (Mar 31 2018 at 08:43):
what do you mean?
Kenny Lau (Mar 31 2018 at 08:43):
and how are you going to name the pullback diagram
Mario Carneiro (Mar 31 2018 at 08:44):
three_in
and three_out
?
Kenny Lau (Mar 31 2018 at 08:44):
hmm
Kenny Lau (Mar 31 2018 at 08:44):
ok
Kenny Lau (Mar 31 2018 at 08:46):
def Mor : bool → bool → Type | ff ff := unit | ff tt := bool | tt ff := empty | tt tt := unit
Kenny Lau (Mar 31 2018 at 08:46):
do you want to switch bool
and empty
?
Mario Carneiro (Mar 31 2018 at 08:49):
Hm, that seems a bit painful to work with
Kenny Lau (Mar 31 2018 at 08:49):
how would you do it?
Kenny Lau (Mar 31 2018 at 08:50):
def Mor : bool → bool → Type | ff ff := unit | ff tt := bool | tt ff := empty | tt tt := unit def Comp : Π x y z : bool, Mor y z → Mor x y → Mor x z | ff ff ff _ g := g | ff ff tt f _ := f | ff tt tt _ g := g | tt tt tt _ _ := ()
Mario Carneiro (Mar 31 2018 at 08:51):
inductive Mor : bool → bool → Type | id : ∀ b, Mor b b | par : bool → Mor ff tt
Kenny Lau (Mar 31 2018 at 08:51):
heh...
Kenny Lau (Mar 31 2018 at 08:51):
what does par
stand for?
Mario Carneiro (Mar 31 2018 at 08:51):
parallel
Mario Carneiro (Mar 31 2018 at 08:51):
making up names is hard...
Kenny Lau (Mar 31 2018 at 08:52):
eu entendo
Mario Carneiro (Mar 31 2018 at 08:54):
It might be worth generalizing this example to a whole bouquet of parallel arrows indexed by A
Kenny Lau (Mar 31 2018 at 08:54):
fair enough
Kenny Lau (Mar 31 2018 at 08:54):
would you like to do the hard job of naming it for me
Mario Carneiro (Mar 31 2018 at 08:54):
par
:)
Kenny Lau (Mar 31 2018 at 08:54):
wonderful
Kenny Lau (Mar 31 2018 at 08:55):
inductive Mor : bool → bool → Type | id : ∀ b, Mor b b | par : bool → Mor ff tt def Comp : Π x y z : bool, Mor y z → Mor x y → Mor x z | ff ff _ f _ := f | _ tt tt _ g := g
Kenny Lau (Mar 31 2018 at 08:55):
two cases!
Kenny Lau (Mar 31 2018 at 09:04):
namespace par variable α : Type u inductive Mor : bool → bool → Type u | id : ∀ b, Mor b b | par : α → Mor ff tt def Comp : Π x y z, Mor α y z → Mor α x y → Mor α x z | ff ff _ f _ := f | _ tt tt _ g := g def Hid_left : ∀ x y f, Comp α x y y (Mor.id α y) f = f | ff ff (Mor.id α b) := rfl | ff tt _ := rfl | tt tt (Mor.id α b) := rfl def Hid_right : ∀ x y f, Comp α x x y f (Mor.id α x) = f | ff ff (Mor.id α b) := rfl | ff tt _ := rfl | tt tt (Mor.id α b) := rfl def Hassoc : ∀ x y z w f g h, Comp α x y w (Comp α y z w f g) h = Comp α x z w f (Comp α x y z g h) | ff ff _ _ _ _ _ := rfl | ff tt tt tt _ _ _ := rfl | tt tt tt tt _ _ _ := rfl end par def par (α : Type u) : category bool := { Mor := par.Mor α, Comp := par.Comp α, Id := par.Mor.id α, Hid_left := par.Hid_left α, Hid_right := par.Hid_right α, Hassoc := par.Hassoc α } def two_mor : category bool := par bool
Kenny Lau (Mar 31 2018 at 09:04):
@Mario Carneiro do you see any possible golf on the casings?
Mario Carneiro (Mar 31 2018 at 09:07):
I sometimes use secondary cases when a bunch of cases are the same; there isn't too much of that in this example but you could write Hassoc
as
def Hassoc : ∀ x y z w f g h, Comp α x y w (Comp α y z w f g) h = Comp α x z w f (Comp α x y z g h) | ff ff _ _ _ _ _ := rfl | b tt tt tt _ _ _ := by cases b; refl
Kenny Lau (Mar 31 2018 at 09:08):
well...
Kenny Lau (Mar 31 2018 at 09:08):
that tactic was |unnecessary|
Mario Carneiro (Mar 31 2018 at 09:09):
The advantage really shows itself when you need to deal with a wildcard case which abbreviates five identical cases with five identical proofs
Kenny Lau (Mar 31 2018 at 09:09):
I see
Mario Carneiro (Mar 31 2018 at 09:10):
it doesn't make the generated proof any shorter, but it's a slightly neater arrangement
Kenny Lau (Mar 31 2018 at 09:10):
structure category (α : Type u) : Type (max u v + 1) := (Mor : Π x y : α, Type v) (Comp : Π x y z, Mor y z → Mor x y → Mor x z) (Id : Π x, Mor x x) (Hid_left : ∀ x y (f : Mor x y), Comp _ _ _ (Id _) f = f) (Hid_right : ∀ x y (f : Mor x y), Comp _ _ _ f (Id _) = f) (Hassoc : ∀ x y z w (f : Mor z w) (g : Mor y z) (h : Mor x y), Comp _ _ _ (Comp _ _ _ f g) h = Comp _ _ _ f (Comp _ _ _ g h))
Kenny Lau (Mar 31 2018 at 09:10):
I run into type problem when I try to define a cone
Kenny Lau (Mar 31 2018 at 09:10):
namely, option
Kenny Lau (Mar 31 2018 at 09:11):
the problem is the type of the morphism...
Mario Carneiro (Mar 31 2018 at 09:11):
I was thinking about doing the same for the Mor.id
cases in Hid_left
and such but you need the cases there so that the ff tt
case works
Kenny Lau (Mar 31 2018 at 09:12):
wait
Kenny Lau (Mar 31 2018 at 09:12):
def Mor : option α → option α → Type u_1 | none none := punit | none (some y) := punit | (some x) none := ulift empty | (some x) (some y) := C.Mor x y
Kenny Lau (Mar 31 2018 at 09:12):
how the hell does this work
Kenny Lau (Mar 31 2018 at 09:12):
variables {α : Type u} (C : category α)
Mario Carneiro (Mar 31 2018 at 09:12):
what's the problem?
Kenny Lau (Mar 31 2018 at 09:12):
this is amazing
Kenny Lau (Mar 31 2018 at 09:12):
I never defined u_1
Kenny Lau (Mar 31 2018 at 09:12):
it comes with C
, but I never gave it a name
Mario Carneiro (Mar 31 2018 at 09:13):
yeah, it's a really thin abstraction
Kenny Lau (Mar 31 2018 at 09:13):
how does it work
Mario Carneiro (Mar 31 2018 at 09:13):
also why variables A B : Sort*
doesn't work
Kenny Lau (Mar 31 2018 at 09:13):
it's unprecedented, the ability to have an unnamed name
Mario Carneiro (Mar 31 2018 at 09:14):
Whenever you have a free universe variable in a variable
declaration, it adds a universe u_n
definition to the file and uses that
Mario Carneiro (Mar 31 2018 at 09:14):
it's not magically named or a metavariable or anything
Kenny Lau (Mar 31 2018 at 09:15):
inductive Mor : option α → option α → Type (max u u_1) | id : ∀ y, Mor none y | mor : ∀ x y (f : C.Mor x y), Mor (some x) (some y)
Kenny Lau (Mar 31 2018 at 09:15):
why do I need max
here but not that one?
Kenny Lau (Mar 31 2018 at 09:15):
def Mor : option α → option α → Type u_1 | none none := punit | none (some y) := punit | (some x) none := ulift empty | (some x) (some y) := C.Mor x y
Mario Carneiro (Mar 31 2018 at 09:17):
I don't recommend using u_1
, you should name your variables
Kenny Lau (Mar 31 2018 at 09:17):
how would I do that?
Mario Carneiro (Mar 31 2018 at 09:17):
universes u v
?
Kenny Lau (Mar 31 2018 at 09:18):
no...
Kenny Lau (Mar 31 2018 at 09:18):
structure category (α : Type u) : Type (max u v + 1) := (Mor : Π x y : α, Type v) (Comp : Π x y z, Mor y z → Mor x y → Mor x z) (Id : Π x, Mor x x) (Hid_left : ∀ x y (f : Mor x y), Comp _ _ _ (Id _) f = f) (Hid_right : ∀ x y (f : Mor x y), Comp _ _ _ f (Id _) = f) (Hassoc : ∀ x y z w (f : Mor z w) (g : Mor y z) (h : Mor x y), Comp _ _ _ (Comp _ _ _ f g) h = Comp _ _ _ f (Comp _ _ _ g h))
Kenny Lau (Mar 31 2018 at 09:18):
I can't name it
Kenny Lau (Mar 31 2018 at 09:18):
it comes with the Mor
of the category variable
Kenny Lau (Mar 31 2018 at 09:18):
variables {α : Type u} (C : category α)
Mario Carneiro (Mar 31 2018 at 09:19):
You can write universe arguments of a constant explicitly with category.{u v}
Kenny Lau (Mar 31 2018 at 09:19):
oh worked
Kenny Lau (Mar 31 2018 at 09:21):
inductive Mor : option α → option α → Type (max u v) | id : ∀ y, Mor none y | mor : ∀ x y (f : C.Mor x y), Mor x y
Kenny Lau (Mar 31 2018 at 09:21):
@Mario Carneiro the name id
is morally wrong; how would you name it?
Kenny Lau (Mar 31 2018 at 09:21):
maybe proj
Mario Carneiro (Mar 31 2018 at 09:22):
you should use some
instead of the coercion
Mario Carneiro (Mar 31 2018 at 09:22):
like you were before
Kenny Lau (Mar 31 2018 at 09:23):
inductive Mor : option α → option α → Type (max u v) | proj : ∀ y, Mor none y | mor : ∀ x y (f : C.Mor x y), Mor (some x) (some y)
Kenny Lau (Mar 31 2018 at 09:23):
this?
Kenny Lau (Mar 31 2018 at 09:23):
komu eesu?
Mario Carneiro (Mar 31 2018 at 09:23):
maybe just none_le
or something like that?
Kenny Lau (Mar 31 2018 at 09:23):
i don't like thinking it in a poset-manner
Kenny Lau (Mar 31 2018 at 09:23):
about
Mario Carneiro (Mar 31 2018 at 09:25):
I think this one should be defined with a def
; that will fix the universe max
thing
Kenny Lau (Mar 31 2018 at 09:25):
what do you mean by "fix", is it a bug?
Mario Carneiro (Mar 31 2018 at 09:26):
The problem is that lean isn't smart enough to notice that the ∀ y
in proj only fills out one morphism per type in the family
Mario Carneiro (Mar 31 2018 at 09:27):
If you just try to push it all in a single inductive type, it overestimates the best universe
Kenny Lau (Mar 31 2018 at 09:32):
def Mor : option α → option α → Type v | none none := punit | none (some y) := punit | (some x) none := ulift empty | (some x) (some y) := C.Mor x y def Comp : Π x y z, Mor C y z → Mor C x y → Mor C x z | none none _ f _ := f | none _ (some z) _ _ := punit.star | (some x) (some y) (some z) f g := C.Comp x y z f g
Kenny Lau (Mar 31 2018 at 09:32):
the sky is blue again
Kenny Lau (Mar 31 2018 at 09:33):
@Mario Carneiro what next, are you going to tell me that you can generalize option
to sum
Mario Carneiro (Mar 31 2018 at 09:34):
def Mor : option α → option α → Type v | none y := punit | _ none := ulift empty | (some x) (some y) := C.Mor x y
Kenny Lau (Mar 31 2018 at 09:34):
ok thanks
Kenny Lau (Mar 31 2018 at 09:35):
ok it's really 3 equations
Mario Carneiro (Mar 31 2018 at 09:35):
You can generalize option to sum, there are two interesting structures there - disjoint union and union where the left objects have morphisms to the right
Mario Carneiro (Mar 31 2018 at 09:36):
but option
is nicer to use than sum punit
Kenny Lau (Mar 31 2018 at 09:36):
def Mor : option α → option α → Type v | none y := punit | (some x) none := ulift empty | (some x) (some y) := C.Mor x y /- ._eqn_1 : ∀ {α : Type u} (C : category α) (y : option α), Mor C none y = punit ._eqn_2 : ∀ {α : Type u} (C : category α) (x : α), Mor C (some x) none = ulift empty ._eqn_3 : ∀ {α : Type u} (C : category α) (x y : α), Mor C (some x) (some y) = C.Mor x y -/ def Mor : option α → option α → Type v | none y := punit | _ none := ulift empty | (some x) (some y) := C.Mor x y /- ._eqn_1 : ∀ {α : Type u} (C : category α), Mor C none none = punit ._eqn_2 : ∀ {α : Type u} (C : category α) (val : α), Mor C none (some val) = punit ._eqn_3 : ∀ {α : Type u} (C : category α) (val : α), Mor C (some val) none = ulift empty ._eqn_4 : ∀ {α : Type u} (C : category α) (x y : α), Mor C (some x) (some y) = C.Mor x y -/
Kenny Lau (Mar 31 2018 at 09:37):
por que pasa isso
Kenny Lau (Mar 31 2018 at 09:37):
ok I know why I'm just exclaiming
Mario Carneiro (Mar 31 2018 at 09:38):
¡
Kenny Lau (Mar 31 2018 at 09:40):
pojish einteindeh si eu falu assii?
Kenny Lau (Mar 31 2018 at 09:41):
@Mario Carneiro
Mario Carneiro (Mar 31 2018 at 09:42):
your portuguese is getting harder to read
Kenny Lau (Mar 31 2018 at 09:42):
:P
Kenny Lau (Mar 31 2018 at 09:42):
"podes entender se eu falo assim"
Mario Carneiro (Mar 31 2018 at 09:43):
I think Nima had a similar issue the other day
Kenny Lau (Mar 31 2018 at 09:43):
not understanding my portuguese?
Mario Carneiro (Mar 31 2018 at 09:44):
something about the order of case splits causing superfluous splitting
Kenny Lau (Mar 31 2018 at 09:44):
oh
Kenny Lau (Mar 31 2018 at 09:44):
I could do disjoint union
Kenny Lau (Mar 31 2018 at 09:44):
but I don't think there's this notion in the category theory community?
Mario Carneiro (Mar 31 2018 at 09:45):
Have you coordinated with Scott?
Kenny Lau (Mar 31 2018 at 09:45):
I mean, it is probably some universal objects in the category of categories
Kenny Lau (Mar 31 2018 at 09:45):
no
Mario Carneiro (Mar 31 2018 at 09:45):
The disjoint union is certainly the coproduct in Cat
Kenny Lau (Mar 31 2018 at 09:45):
aha
Kenny Lau (Mar 31 2018 at 09:48):
is there a shorthand for punit.star
?
Kenny Lau (Mar 31 2018 at 09:57):
why does #check Type 1000000
crash?
Mario Carneiro (Mar 31 2018 at 09:57):
no, although you could open punit
Mario Carneiro (Mar 31 2018 at 09:58):
lol stop breaking lean
Kenny Lau (Mar 31 2018 at 09:58):
I mean, it should be just a construct
Kenny Lau (Mar 31 2018 at 09:58):
unless you're telling me that it creates an array with a million entries
Kenny Lau (Mar 31 2018 at 09:58):
but most of them should be just empty?
Mario Carneiro (Mar 31 2018 at 09:58):
I think it does something by recursion a million times and busts the stack
Kenny Lau (Mar 31 2018 at 09:59):
hmm
Kenny Lau (Mar 31 2018 at 09:59):
Is there a general construction that abstracts "from set to pointed set"?
Kenny Lau (Mar 31 2018 at 10:02):
oh, it's just the coslice category isn't it
Kenny Lau (Mar 31 2018 at 10:38):
def Cat : category Σ α : Type u, category.{u v} α := { Mor := λ C D, functor C.2 D.2, Comp := λ C D E, functor.comp C.2 D.2 E.2, Id := λ C, functor.id C.2, Hid_left := λ C D F, by cases F; refl, Hid_right := λ C D F, by cases F; refl, Hassoc := λ _ _ _ _ _ _ _, rfl, }
Kenny Lau (Mar 31 2018 at 10:38):
my life is now complete
Kenny Lau (Mar 31 2018 at 11:24):
def comma {α : Type u} (C : category.{u v} α) {β : Type u₁} (D : category.{u₁ v₁} β) {γ : Type u₂} (E : category.{u₂ v₂} γ) (F : functor C E) (G : functor D E) : category Σ c d, E.Mor (F.F c) (G.F d) := { Mor := λ x y, { P : C.Mor x.1 y.1 × D.Mor x.2.1 y.2.1 // E.Comp (F.F x.1) (F.F y.1) (G.F y.2.1) y.2.2 (F.mor x.1 y.1 P.1) = E.Comp (F.F x.1) (G.F x.2.1) (G.F y.2.1) (G.mor x.2.1 y.2.1 P.2) x.2.2 }, Comp := λ x y z P Q, ⟨(C.Comp x.1 y.1 z.1 P.1.1 Q.1.1, D.Comp x.2.1 y.2.1 z.2.1 P.1.2 Q.1.2), by rw [← F.Hcomp, ← G.Hcomp, E.Hassoc, ← Q.2, ← E.Hassoc, P.2, E.Hassoc]⟩, Id := λ x, ⟨(C.Id x.1, D.Id x.2.1), by rw [F.Hid, G.Hid, E.Hid_left, E.Hid_right]⟩, Hid_left := λ x y P, subtype.eq $ by dsimp; rw [C.Hid_left, D.Hid_left]; cases P.1; refl, Hid_right := λ x y P, subtype.eq $ by dsimp; rw [C.Hid_right, D.Hid_right]; cases P.1; refl, Hassoc := λ x y z w P Q R, subtype.eq $ by dsimp; rw [C.Hassoc, D.Hassoc] }
Now I can have pointed sets :P
Nima (Mar 31 2018 at 19:41):
How do we pass arguments to meta definition?
The following does not typecheck
universe u open tactic monad expr classical meta def inst {α: Type u} (p:α → Prop) (h:∀ (n:α), p n) (a:α) : tactic unit := do skip example {α:Type u} (b:α) (p:α → Prop) : (∀ (a:α), p a) → p b := begin intro h, have h' : p b, from h b, -- I want the next line have does the same thing inst p h b, -- error: unknown identifier 'p' 'h' 'b' end
Tactic State:
α : Type u, b : α, p : α → Prop, h : ∀ (a : α), p a, h' : p b ⊢ p b
Simon Hudon (Mar 31 2018 at 19:43):
I believe there exists a tactic for what you're trying to do. I'm still going to assume that you want an answer to the question you actually asked
Nima (Mar 31 2018 at 19:45):
I don't know such tactic, but sure, I would like to know an answer to what I asked.
Simon Hudon (Mar 31 2018 at 19:48):
It is called specialize
. It is pretty nice.
Let's put your tactic in its section because we need to open a bunch of namespaces:
section open tactic interactive interactive.types lean.parser meta def inst (fa : parse texpr) (x : parse texpr) : tactic unit := do trace fa, trace x end
Simon Hudon (Mar 31 2018 at 19:49):
When you're given information to a tactic, you're basically giving them syntactic objects and you need provide a way for Lean to parse it. That's because you can invent a lot of different notations for your tactics.
Nima (Mar 31 2018 at 19:51):
When I write inst h b
, I still get the same error(?)
Simon Hudon (Mar 31 2018 at 19:53):
I'm not sure it will solve anything but let's see if it does. Add the following between your tactic and your lemma:
run_cmd add_interactive [`inst]
Nima (Mar 31 2018 at 19:55):
Instead of the previous error, this gives me error expression expected
, error is below b
in inst h b
Nima (Mar 31 2018 at 20:00):
Also, is there anyway I can use specialize
on a goal like ∃ (b : α), p b
Simon Hudon (Mar 31 2018 at 20:01):
To your second question: no, you need existsi
and you feel it your witness or a list of witnesses
Simon Hudon (Mar 31 2018 at 20:02):
What if you erase b
in inst h b
?
Nima (Mar 31 2018 at 20:04):
Here is the status:
universe u section open tactic interactive interactive.types lean.parser meta def inst (fa : parse texpr) (x : parse texpr) : tactic unit := do trace fa, trace x end run_cmd add_interactive [`inst] example {α:Type u} (b:α) (p:α → Prop) : (∀ (a:α), p a) → p b := begin intro h, inst h, -- error: expression expected admit end
Simon Hudon (Mar 31 2018 at 20:05):
Ok, I get it. Sorry it took me a while. Lean seems h b
as one expression in inst h b
so we need a separator
Simon Hudon (Mar 31 2018 at 20:06):
We could require the user to enter inst (h, b)
, how does that look to you?
Nima (Mar 31 2018 at 20:07):
Fine by me, but does that mean we should change definition of inst
? (right now I am getting the same error)
Simon Hudon (Mar 31 2018 at 20:07):
In that case, you'd define your tactic as:
meta def inst (fa : parse $ tk "(" *> texpr <* tk ",") (x : parse $ texpr <* tk ")") : tactic unit := do trace fa, trace x end
Nima (Mar 31 2018 at 20:07):
wow! this is scary
Simon Hudon (Mar 31 2018 at 20:08):
tk
and texpr
are commands for the parser: literally "parse a parenthesis" and "parse an expression"
Simon Hudon (Mar 31 2018 at 20:10):
<*
and *>
is just to get them to be executed one after the other. a <* b
runs a
first, then b
and returns whatever a
returns. a *> b
runs a
first as well and then runs b
and returns the result of b
Nima (Mar 31 2018 at 20:11):
I see, and $
?
Simon Hudon (Mar 31 2018 at 20:13):
It's a way of making function application nicer by using fewer parentheses. If you'd write f (g (h x))
instead you can write f $ g $ h x
Simon Hudon (Mar 31 2018 at 20:14):
So, instead of parse $ tk "(" *> texpr <* tk ","
, one could write:
parse (do tk "(", e ← texpr, tk ",", return e)
Nima (Mar 31 2018 at 20:15):
We can only use $
in meta world. Right?
The following gives me error
def f (n:ℕ) := n+1 example : f $ f $ 1 = 3 := sorry
Error is:
type mismatch at application f (1 = 3) term 1 = 3 has type Prop but is expected to have type ℕ
Simon Hudon (Mar 31 2018 at 20:17):
No actually, it works everywhere. The problem with your expression is f $ f 1
is of type ℕ
but you're using it to specify the type of example
it should have type Sort u
Simon Hudon (Mar 31 2018 at 20:18):
Now the problem is precedence: $
has lower precedence than =
and Lean reads your expression as f (f (1 = 3))
Nima (Mar 31 2018 at 20:18):
Nice, thanks a lot,
Also, existsi
you mentioned worked as a charm.
Simon Hudon (Mar 31 2018 at 20:19):
$
won't make your expression nicer but if you applied f
once more, you could write f (f $ f 1) = 3
where you use (
and )
to "protect" =
from $
, in a sense
Nima (Mar 31 2018 at 20:22):
I see, it is good to have an alternative to parenthesis
Moses Schönfinkel (Mar 31 2018 at 20:23):
to be fair I would then do example : (f ∘ f ∘ f) 1 = 4 := sorry
:)
Moses Schönfinkel (Mar 31 2018 at 20:24):
it's an eternal Haskell bikeshed as to how to mix parens, composition, flip and low priority application :P
Simon Hudon (Mar 31 2018 at 20:33):
I agree on second point, not on first. I prefer f ∘ f ∘ f $ 1
if we're going to use ∘
to avoid parenthesis ... but in this case, it doesn't help
Moses Schönfinkel (Mar 31 2018 at 20:34):
oh, you're absolutely right in this regard, it doesn't avoid the parens :)
Moses Schönfinkel (Mar 31 2018 at 20:34):
in general I do prefer composition over anything else tho
Nima (Apr 08 2018 at 03:14):
I know exact fact p
finishes the proof, but how can I break it into the following two steps:
1. add fact
to my set of hypotheses (named fa
)
2. use fa
to finish the proof
constant fact : ∀ (p:Prop), ¬p example (p:Prop) : ¬p := begin exact fact p end
Also, I used constant
to define an axiom. Is this the usual method?
Kenny Lau (Apr 08 2018 at 03:15):
we don't usually use constant/axiom
Mario Carneiro (Apr 08 2018 at 03:15):
have fa := fact, exact fa p
Mario Carneiro (Apr 08 2018 at 03:16):
The usual method is not to add axioms at all. Usually it suffices to use variable
instead, which adds the "axiom" as a precondition of the theorem
Mario Carneiro (Apr 08 2018 at 03:17):
You can also use apply fa
instead of exact fa p
to finish the proof
Kenny Lau (Apr 08 2018 at 03:18):
as a useless and obvious remark, your axiom is inconsistent:
Kenny Lau (Apr 08 2018 at 03:18):
constant fact : ∀ (p:Prop), ¬p example : false := begin apply fact (fact = fact), exact rfl end
Nima (Apr 08 2018 at 03:18):
If I have many axioms, isn't it going to be a problem?
Suppose I define a lot of axioms to use in a project.
But I am sure I am not going to use all of them in all of theorems.
Isn't it considered a problem that I see all of them in my proof status?
Kenny Lau (Apr 08 2018 at 03:19):
what are your axioms?
Mario Carneiro (Apr 08 2018 at 03:20):
One way to manage a large collection of axioms is to bundle them all in a typeclass like field A
Mario Carneiro (Apr 08 2018 at 03:21):
If you use variable
to declare your axioms, only the ones you actually use in a given theorem will be added as preconditions to the theorem
Nima (Apr 08 2018 at 03:22):
If you use
variable
to declare your axioms, only the ones you actually use in a given theorem will be added as preconditions to the theorem
So in that case, what is the difference between constant and variable?
Kenny Lau (Apr 08 2018 at 03:24):
constant
is global, in the sense that it is true once and for all
Kenny Lau (Apr 08 2018 at 03:25):
whereas, e.g. example : P -> Q := sorry
is the same as
variable (h : P) example : Q := sorry
Kenny Lau (Apr 08 2018 at 03:25):
variable
is more like a local assumption
Nima (Apr 08 2018 at 03:26):
I have not written any large collection of axioms, I am thinking of a C++
type with a lot of constraints.
If all these constraints are satisfied then the operations that I will define on this type make sense.
So how should I define all type constraints so I can easily use them later? (obviously I am going to have more than one type)
Kenny Lau (Apr 08 2018 at 03:27):
class module (α : out_param $ Type u) (β : Type v) [out_param $ ring α] extends has_scalar α β, add_comm_group β := (smul_add : ∀r (x y : β), r • (x + y) = r • x + r • y) (add_smul : ∀r s (x : β), (r + s) • x = r • x + s • x) (mul_smul : ∀r s (x : β), (r * s) • x = r • s • x) (one_smul : ∀x : β, (1 : α) • x = x)
Kenny Lau (Apr 08 2018 at 03:27):
make it a structure / class
Simon Hudon (Apr 08 2018 at 03:30):
A constant
/ axiom
(they're the same) is like you gained knowledge for free. With variables
, class
and structure
, you get to assume certain properties but whatever you assume eventually has to be proved.
Simon Hudon (Apr 08 2018 at 03:33):
If you have variable h : false
, you can prove any theorem statement you want but using that theorem will be much more demanding. If you have constant h : false
, you'll be able to prove all the theorems that you want and you'll never have to "pay" for such a strong assumption. That means that you may be building a bunch of nonsense. Especially if you have two or more axioms and you don't realize that they contradict each other
Simon Hudon (Apr 08 2018 at 03:36):
(actually, I can't think of a use for a theorem that assumes false (with variable
), it's probably useless but also harmless)
Kenny Lau (Apr 08 2018 at 03:36):
you can never prove false, so you can never use that theorem
Kenny Lau (Apr 08 2018 at 03:37):
oh wait
Nima (Apr 08 2018 at 03:37):
So would you finish this example (if I use constant
then I can do it):
variable h : false example : ∀ (p:Prop), ¬ p := sorry
Kenny Lau (Apr 08 2018 at 03:37):
that theorem is exactly false.elim
Mario Carneiro (Apr 08 2018 at 03:37):
I use false.elim
all the time...
Kenny Lau (Apr 08 2018 at 03:37):
false.elim h
Simon Hudon (Apr 08 2018 at 03:37):
I use
false.elim
all the time...
Yes, me too but you only need one of those
Mario Carneiro (Apr 08 2018 at 03:38):
What is true is that you don't really need anything other than false.elim
in an inconsistent context
Nima (Apr 08 2018 at 03:38):
I see, thanks
Mario Carneiro (Apr 08 2018 at 03:38):
since it's like sorry
, it's the best theorem
Mario Carneiro (Apr 08 2018 at 03:39):
You can also write h.elim
if h : false
btw
Nima (Apr 08 2018 at 03:40):
When I wasn't using class
, I defined a constant for an operator and then define notation for it. How can I do the same for class?
universe u class number(α : Type u) [linear_order α] := (neg₀ : α → option α) prefix `-₀`:40 := neg₀ -- won't type check
Kenny Lau (Apr 08 2018 at 03:40):
number.neg_o
Kenny Lau (Apr 08 2018 at 03:41):
not sure what you're trying to do though
Kenny Lau (Apr 08 2018 at 03:41):
neg maps alpha to option alpha?
Mario Carneiro (Apr 08 2018 at 03:41):
optional negation, I assume
Mario Carneiro (Apr 08 2018 at 03:42):
something like nat.ppred
?
Nima (Apr 08 2018 at 03:42):
Excellent,
Suppose I have a value of type int
(in C++
). Just because negation is defined it does not mean that I can take negation of every value. So I defined it to be something like this!
Kenny Lau (Apr 08 2018 at 03:42):
but shouldn't that not be a field
Kenny Lau (Apr 08 2018 at 03:43):
I mean, you don't want to permit any map from alpha to option alpha
Mario Carneiro (Apr 08 2018 at 03:43):
Who said anything about it being a field?
Kenny Lau (Apr 08 2018 at 03:43):
by field I mean a field of the class
Kenny Lau (Apr 08 2018 at 03:43):
i.e. the things after :=
Mario Carneiro (Apr 08 2018 at 03:43):
what's wrong with specifying a negation operation like this?
Kenny Lau (Apr 08 2018 at 03:44):
oh
Kenny Lau (Apr 08 2018 at 03:44):
I thought it's like making int from nat
Kenny Lau (Apr 08 2018 at 03:44):
misinterpreted
Nima (Apr 08 2018 at 03:50):
Section 6.6 of Theorem Proving in Lean says
They can also include subscripts, which can be entered by typing
\_
followed by the desired subscripted character
I can only use single digits as subscript in VS Code. Am I missing something?
Kenny Lau (Apr 08 2018 at 03:50):
\_m\_g
Kenny Lau (Apr 08 2018 at 03:50):
you need to do it twice
Nima (Apr 08 2018 at 03:53):
I type m
then \
then _
them m
then \
then _
then g
then space
I got: m
followed by a box followed by a dash followed by g
Kenny Lau (Apr 08 2018 at 03:54):
\_m \_g
Kenny Lau (Apr 08 2018 at 03:54):
space after m
Nima (Apr 08 2018 at 03:55):
I typed m
then \
then _
them m
then space then \
then _
then g
then space
I got: m
followed by a box followed by a space followed by dash followed by g
Kenny Lau (Apr 08 2018 at 03:56):
maybe m
doesn't work then
Kenny Lau (Apr 08 2018 at 03:56):
\_r \_g
Kenny Lau (Apr 08 2018 at 03:56):
not every letter has subscript
Kenny Lau (Apr 08 2018 at 03:57):
the point being, make each letter separately
Nima (Apr 08 2018 at 03:58):
Thank, r
worked
Nima (Apr 08 2018 at 04:02):
Would you let me know why the following won't type check
universe u class number(α : Type u) [linear_order α] := (neg₀ : α → option α) prefix `-₀`:40 := number.neg₀ example (α : Type u) [linear_order α] : ∀ (n:number α), (-₀ n) = (neg₀ n) := sorry
Kenny Lau (Apr 08 2018 at 04:02):
number.neg_0 n
Kenny Lau (Apr 08 2018 at 04:02):
or n.neg_0
Nima (Apr 08 2018 at 04:05):
oops!
When I defined everything in terms of a class, then type of my operators don't make any sense
Kenny Lau (Apr 08 2018 at 04:05):
how so?
Nima (Apr 08 2018 at 04:06):
Ok, here is two errors I still receive
failed to synthesize type class instance for α : Type u, _inst_1 : linear_order α, n : number α ⊢ linear_order (number α)
invalid field notation, function 'number.neg₀' does not have explicit argument with type (number ...)
Kenny Lau (Apr 08 2018 at 04:06):
right\
Kenny Lau (Apr 08 2018 at 04:06):
aha
Kenny Lau (Apr 08 2018 at 04:06):
you should have [number \alpha]
Kenny Lau (Apr 08 2018 at 04:07):
and then \forall n:\alpha, -\_o n = number.neg\_o n
Kenny Lau (Apr 08 2018 at 04:08):
but I would use extends
:
universe u class number (α : Type u) extends linear_order α := (neg₀ : α → option α) prefix `-₀`:40 := number.neg₀ example (α : Type u) [number α] : ∀ (n:α), (-₀ n) = (number.neg₀ n) := λ _, rfl
Simon Hudon (Apr 08 2018 at 04:08):
When going from an OO-ish language (like C++) to a functional language (like Lean and Haskell), one pitfall is that class
don't mean the same thing anymore. In C++, a class defines a type while in Lean, a class is a sort of packet of information that can be inferred about your types
Nima (Apr 08 2018 at 04:12):
Thanks, without [linear_order α]
--> extends linear_order α
it would not type checked
Kenny Lau (Apr 08 2018 at 04:12):
there's a way to do it without extends
:
universe u class number (α : Type u) [linear_order α] := (neg₀ : α → option α) prefix `-₀`:40 := number.neg₀ example (α : Type u) [linear_order α] [number α] : ∀ (n:α), (-₀ n) = (number.neg₀ n) := λ _, rfl
Kenny Lau (Apr 08 2018 at 04:12):
but I prefer the version with extends
Nima (Apr 08 2018 at 04:13):
Also, can I do something so instead of _inst_1 : number α
I will get something like hn : number α
?
Kenny Lau (Apr 08 2018 at 04:14):
if you want to do that, you can, but you should also make it a structure
Kenny Lau (Apr 08 2018 at 04:15):
universe u class number (α : Type u) [linear_order α] := (neg₀ : α → option α) prefix `-₀`:40 := number.neg₀ example (α : Type u) [linear_order α] [hn : number α] : ∀ (n:α), (-₀ n) = (number.neg₀ n) := λ _, rfl
Nima (Apr 08 2018 at 04:16):
Awesome, thank you
Nima (Apr 08 2018 at 04:52):
Sorry if it is too obvious, but how do you finish this example:
universe u class number(α : Type u) extends linear_order α := unit example (α : Type u) [nn:number α] (a:α) (b:α) : (a<b) → (a≤ b) := sorry
Mario Carneiro (Apr 08 2018 at 04:53):
le_of_lt
Kenny Lau (Apr 08 2018 at 04:53):
and.left
:-)
Mario Carneiro (Apr 08 2018 at 04:53):
eww
Mario Carneiro (Apr 08 2018 at 04:54):
actually that won't work now that I come to think of it
Nima (Apr 08 2018 at 04:55):
Thanks, but I was thinking of how I can directly use preorder.lt
?
When I say have hh:=nn.lt
all I get is hh : α → α → Prop
. But preorder.lt
is defined to be (lt := λ a b, a ≤ b ∧ ¬ b ≤ a)
Kenny Lau (Apr 08 2018 at 04:56):
use let
instead of have
Kenny Lau (Apr 08 2018 at 04:56):
have
forgets definitions
Mario Carneiro (Apr 08 2018 at 04:56):
This was also kenny's mistake - preorder.lt
is not defined as that
Mario Carneiro (Apr 08 2018 at 04:56):
that's only the default value
Mario Carneiro (Apr 08 2018 at 04:56):
there is a second field that says that preorder.lt
is equivalent to that, from which le_of_lt
is proven
Mario Carneiro (Apr 08 2018 at 04:58):
Perhaps you are oversimplifying your problem, but le_of_lt
is certainly the way to solve the original question
Nima (Apr 08 2018 at 05:03):
sure, I am trying to see how I can directly use all those properties defined for a class.
I used let hh := nn.lt
and got hh : α → α → Prop := linear_order.lt
.
Why does it say linear_order
and not preorder
?
class preorder (α : Type u) extends has_le α, has_lt α := (le_refl : ∀ a : α, a ≤ a) (le_trans : ∀ a b c : α, a ≤ b → b ≤ c → a ≤ c) (lt := λ a b, a ≤ b ∧ ¬ b ≤ a) (lt_iff_le_not_le : ∀ a b : α, a < b ↔ (a ≤ b ∧ ¬ b ≤ a) . order_laws_tac) class partial_order (α : Type u) extends preorder α := (le_antisymm : ∀ a b : α, a ≤ b → b ≤ a → a = b) class linear_order (α : Type u) extends partial_order α := (le_total : ∀ a b : α, a ≤ b ∨ b ≤ a)
Mario Carneiro (Apr 08 2018 at 05:15):
linear_order.lt
extracts the lt
field of a linear_order
. It is defined in terms of preorder.lt
, just composing with the parent structure conversions
Nima (Apr 08 2018 at 05:16):
When lt_iff_le_not_le
is defined for preorder
, why do we need a lemma with same name for preorder
?
Mario Carneiro (Apr 08 2018 at 05:17):
If you know number α
, then you can write number.lt a b
where a b : α
(you don't need to refer to nn
) and it will refer to the lt
field inherited from preorder
Mario Carneiro (Apr 08 2018 at 05:18):
The lemmas of a structure are often restated as separate theorems in order to get the notations and implicitness of arguments right
Mario Carneiro (Apr 08 2018 at 05:19):
so for instance you can write a < b
where a b : α
and it will find the number
instance and work it back to the preorder
that supplies the implementation of <
Mario Carneiro (Apr 08 2018 at 05:20):
but this is not the same term as number.lt a b
(it is definitionally equal), which can affect rewrites and things
Nima (Apr 08 2018 at 05:20):
let hh := number.lt a b
gives me error: unknown identifier 'number.lt'
Mario Carneiro (Apr 08 2018 at 05:21):
The new structure command doesn't create number.lt
type fields, it just has parent structure fields
Mario Carneiro (Apr 08 2018 at 05:21):
the preferred way to refer to it is a < b
of course
Mario Carneiro (Apr 08 2018 at 05:22):
but should be able to write preorder.lt a b
or linear_order.lt a b
.. they are all the same, definitionally
Nima (Apr 08 2018 at 05:26):
I have no problem with a<b
. My problem is that I still don't know how to finish this example (how do I expand definition nn.lt
or preorder.lt a b
, I failed to do it using rw
)? I don't care about the full proof, just how do I bring definition or the lemma itself into my hypotheses?
universe u class number(α : Type u) extends linear_order α := unit example (α : Type u) [nn:number α] (a:α) (b:α) : (a<b) → (a≤ b) := begin intro less, let hh := nn.lt, admit end
Mario Carneiro (Apr 08 2018 at 05:32):
The relevant lemma is lt_iff_le_not_le
, which you can refer to directly by preorder.lt_iff_le_not_le
or through the restated version (which uses notation for le and lt). So the proof would be something like (preorder.lt_iff_le_not_le _ _).1 less
Mario Carneiro (Apr 08 2018 at 05:34):
Again, nn.lt
is not a definition, despite the :=
. It is a default value for a field, which is allowed to be anything. The reason we know it is in fact equivalent to that default value is because of lt_iff_le_not_le
.
Nima (Apr 08 2018 at 05:46):
Again,
nn.lt
is not a definition, despite t...
Very helpful, thank you.
How can I expand/replace ≤
to/with preorder.le
?
Kenny Lau (Apr 08 2018 at 05:47):
dsimp [( ≤ )]
Nima (Apr 08 2018 at 05:47):
Nice
Nima (Apr 08 2018 at 16:09):
How should I write the match
so the whole thing will type-check?
universe u class number(α : Type u) extends linear_order α := (min : option α) (min_prop : match min with | none := true -- some predicate | sime m := true -- some predicate end)
Kevin Buzzard (Apr 08 2018 at 16:12):
Some not sime
Kenny Lau (Apr 08 2018 at 16:12):
still does not typecheck
Kevin Buzzard (Apr 08 2018 at 16:12):
And brackets round some m
Kevin Buzzard (Apr 08 2018 at 16:12):
That should do it
Kenny Lau (Apr 08 2018 at 16:12):
nope
Kenny Lau (Apr 08 2018 at 16:13):
I don't think you can do funny things inside that particular position
Kevin Buzzard (Apr 08 2018 at 16:13):
Then we need the big guns to save us
Nima (Apr 08 2018 at 16:14):
universe u class number(α : Type u) extends linear_order α := (min : option α) (min_prop : match min with | none := true -- some predicate | (some m) := true -- some predicate end)
gives me invalid match/convoy expression, expected type is not known
Kevin Buzzard (Apr 08 2018 at 16:14):
min might also mean something else
Kenny Lau (Apr 08 2018 at 16:14):
universe u class number(α : Type u) extends linear_order α := (min : option α) (min_prop : @option.rec_on α (λ x, Prop) min true (λ x, true))
Kenny Lau (Apr 08 2018 at 16:14):
this worked
Kevin Buzzard (Apr 08 2018 at 16:14):
He is just rewriting the match into what lean expands it to
Kevin Buzzard (Apr 08 2018 at 16:15):
Given the error
Nima (Apr 08 2018 at 16:16):
To me match
or cases
are a lot more readable that rec_on
.
Is there any way to fix this and use match
?
Kevin Buzzard (Apr 08 2018 at 16:16):
Is the issue that lean needs to be told the type earlier on?
Kenny Lau (Apr 08 2018 at 16:16):
I think so
Nima (Apr 08 2018 at 17:12):
Any way I can fix the error message failed to register private name '_match_2', prefix has not been registered
for this:
universe u class number(α : Type u) extends linear_order α := (min : option α) (max_prop : Prop := match min with | none := true | (some _) := true end))
Simon Hudon (Apr 08 2018 at 17:35):
Interesting. I'm not sure you can use match
this way because it requires the creation of auxiliary definitions. You can try option.cases_on min true (λ _, true)
instead or create a definition yourself that you refer to in that expression.
Nima (Apr 08 2018 at 17:43):
I know option.cases_on min true (λ _, true)
works, but to me match
and cases
are a lot more readable.
I am just trying to define some property to my class
and it seems impossible without using cases_on
or rec_on
Simon Hudon (Apr 08 2018 at 17:47):
You can also do:
def max_prop_def (α : Type*) : option α -> Prop | none := true | (some _) := true
And use max_prop_def min
as your default value.
Nima (Apr 08 2018 at 17:50):
Fantastic, thanks a lot!
Type*
means Type at some universe?
Simon Hudon (Apr 08 2018 at 18:02):
Exactly! And Lean infers that universe for you
Kevin Buzzard (Apr 08 2018 at 18:15):
you can also write universe u
at the top and then (alpha : Type u)
. It's like variables -- you are implicitly quantifying over universes.
Mario Carneiro (Apr 08 2018 at 18:25):
I strongly recommend Simon's solution over inlining the match (even assuming you can get it to work). It might look nice up front, but as soon as you start using these properties in proofs, you will have to reference those internals
Nima (Apr 08 2018 at 18:38):
Would you please tell me how I should change this, so it will type check?
inductive constraint (α:Type*) | trv : constraint | stt (bnd:α) (low:Prop) (d : decidable low) : constraint namespace constraint def setof {α : Type*} [linear_order α] : constraint α → α → Prop | (trv α) a := true | (stt bnd low d) a := if low then bnd<a else a<bnd -- ERROR: failed to synthesize type class instance for
Simon Hudon (Apr 08 2018 at 18:51):
I'm not sure what @Mario Carneiro will think of it but I propose:
inductive bound (α:Type*) | upper (bnd:α) : bound | lower (bnd:α) : bound def check {α:Type*} [decidable_linear_order α] : bound α → α → bool | (bound.lower x) y := x < y | (bound.upper x) y := x > y inductive constraint (α:Type*) | trv : constraint | stt (bnd:bound α) : constraint namespace constraint def setof {α : Type*} [decidable_linear_order α] : constraint α → α → bool | (trv α) a := true | (stt bnd) a := check bnd a
Nima (Apr 08 2018 at 19:02):
Thanks for showing lean
:)
I have to think about/learn/understand your solution.
In a meantime, is there a way to merge similar cases?
Consider the following example
inductive constraint (α:Type*) | trv : constraint | lt (bnd:α) : constraint | le (bnd:α) : constraint | gt (bnd:α) : constraint | ge (bnd:α) : constraint open constraint def prop (α:Type*) : constraint α → Prop | (trv α):= true -- short term | (lt _) := true -- very long term 1 | (le _) := true -- very long term 1 | (gt _) := true -- very long term 2 | (ge _) := true -- very long term 2
Is there any way I use pattern matching and merge those cases where the right hand side is going to be the same?
I know it is a long shot, but even better: may be cases for lt
and le
have a lot in common, very_long ∧p1
and very_long∧p2
. Is there a way to effectively write something like
def prop (α:Type*) : constraint α → Prop | (trv α):= true -- short term | x:(lt _) or x:(le _) := very_long ∧ match x with |(lt _):= p1 | (le _):=p2 end | (gt _) := true -- very long term 2 | (ge _) := true -- very long term 2
Simon Hudon (Apr 08 2018 at 19:21):
Thanks for showing
lean
My pleasure! I love sharing my excitement for Lean.
I would go back to the separation of constraint
and bound
and define prop
as:
inductive strictness | strict | non_strict inductive bound (α:Type*) | upper (bnd:α) : strictness → bound | lower (bnd:α) : strictness → bound open bound strictness -- ... def prop (α:Type*) : constraint α → Prop | (trv α):= true -- short term | (stt (lower b str)) := very_long ∧ match str with | strict:= p1 | non_strict :=p2 end | (stt (upper b str)) := -- very long term 2
Simon Hudon (Apr 08 2018 at 19:23):
So the short answer is that you can't combine pattern matching and propositional notation. You can, however, try to get a bit closer and define functions like is_strict
or is_le
and use them in if _ then _ else _
.
Nima (Apr 08 2018 at 19:32):
Thanks a lot,
About [decidable_linear_order ℕ]
At the intuitive level, does this mean if you give me two natural numbers, I can tell you which one is larger?
I think the answer yes, but it should be no.
For example, if you give me two natural number in binary format then comparing them is decidable. But who said you are going to always give them in binary format?
So if I use [decidable_linear_order ℕ]
in a definition, and use classical
to find two numbers inside that definition, does that mean comparing them is decidable? (I think the answer should be no, but I don't know why)
Simon Hudon (Apr 08 2018 at 19:40):
At the intuitive level, does this mean if you give me two natural numbers, I can tell you which one is larger?
Yes that's correct.
Simon Hudon (Apr 08 2018 at 19:44):
I think the answer yes, but it should be no.
In Lean, no set of numbers are postulated (i.e. we say "let there be natural numbers" and they appear). Instead, each set of numbers is constructed as an inductive type. In particular, natural numbers are defined as follows:
inductive nat | zero : nat | succ : nat -> nat
You may recognize the structure of Peano's axiomatization of natural numbers. The big difference is that the above is a valid definition. Whenever you are given two natural numbers, they are given to you in unary notation (e.g. 11111 for 5, 111 for 3 and so on). This makes comparison decidable
Simon Hudon (Apr 08 2018 at 19:45):
For the sake of performances however, the virtual machine and some tactics use binary representations (in the case of the vm, it's implemented by gmp, I believe)
Simon Hudon (Apr 08 2018 at 19:45):
Is this what you were getting at?
Nima (Apr 08 2018 at 19:59):
That makes sense, thanks.
Nima (Apr 08 2018 at 22:43):
How can I finish the last definition?
open nat inductive ind | emt : ind | val (a:ℕ) :ind open ind def is_nonempty : ind → Prop | emt := false | (val _) := true def valof (i : ind) (h : is_nonempty i) : ℕ := match i with | (val n) := n | _ := begin admit end end
Status is
i : ind, h : is_nonempty i, _match : ind → ℕ, _x : ind ⊢ ℕ
It seems lean
forgot about the first case!
Nima (Apr 08 2018 at 23:05):
I don't understand what the difference is, but it could be done using the following definition:
def valof (i : ind) (h : is_nonempty i) : ℕ := begin cases i, rw is_nonempty at h, contradiction, exact a end
Kevin Buzzard (Apr 08 2018 at 23:08):
Here's how to avoid the a
:
Kevin Buzzard (Apr 08 2018 at 23:09):
def valof (i : ind) (h : is_nonempty i) : ℕ := begin cases i with n, { exact false.elim h }, { exact n } end
Kevin Buzzard (Apr 08 2018 at 23:10):
After cases
you have two goals, and it's encouraged to wrap them in curly brackets so you can deal with them one at a time (it helps with debugging later on when Leo changes everything and stuff stops working)
Nima (Apr 08 2018 at 23:11):
what is wrong with a
?
Is it because a
is used internally?
Kevin Buzzard (Apr 08 2018 at 23:23):
the fact that Lean randomly calls variables a is considered a bug ;-)
Kevin Buzzard (Apr 08 2018 at 23:23):
Not least because you might already have another variable called a!
Kevin Buzzard (Apr 08 2018 at 23:25):
https://github.com/leanprover/lean/issues/1822
Kevin Buzzard (Apr 08 2018 at 23:26):
In Lean 4 your code will stop working but mine should be OK. In fact your code does not work for me -- which version of Lean are you using?
Kevin Buzzard (Apr 08 2018 at 23:26):
I am on the nightly from 6th April.
Kevin Buzzard (Apr 08 2018 at 23:27):
All my code does, of course, is explicitly names the variable when we do the case split.
Nima (Apr 08 2018 at 23:29):
Lean (version 3.3.0, commit fa9c868ed2bb, Release)
Kevin Buzzard (Apr 08 2018 at 23:31):
v3.4 is coming soon. A lot has happened since 3.3.
Kevin Buzzard (Apr 08 2018 at 23:32):
Oh, this works for me:
Kevin Buzzard (Apr 08 2018 at 23:32):
def valof' : Π (i : ind), is_nonempty i → ℕ | (val n) h := n | emt h := false.elim h
Kevin Buzzard (Apr 08 2018 at 23:33):
The equation compiler is apparently more clever than match
Kevin Buzzard (Apr 08 2018 at 23:34):
that can't be right because they are the same thing. The difference is that I am getting h involved in the matching process here.
Kevin Buzzard (Apr 08 2018 at 23:36):
def valof (i : ind) (h : is_nonempty i) : ℕ := match i,h with | (val n), h := n | emt, h := false.elim h end
Kevin Buzzard (Apr 08 2018 at 23:36):
You need to tell the equation compiler about h explicitly, so it seems.
Nima (Apr 08 2018 at 23:57):
Thanks, your solution worked for me,
But I did not understand why I should use h
in the match (I know it won't type-check without it, but I don't know why)
Nima (Apr 09 2018 at 00:09):
How can I mark result Prop decidable?
def func(p:Prop)[decidable p] : Prop := p example (p:Prop)[decidable p] : if (func p) then true else false := sorry -- won't type check
Kevin Buzzard (Apr 09 2018 at 00:17):
def func (p:Prop) : Prop := p instance func_of_decidable_is_decidable (p : Prop) [H : decidable p] : decidable (func p) := H example (p:Prop) [decidable p] : if (func p) then true else false := sorry
Kevin Buzzard (Apr 09 2018 at 00:18):
I explain to the type class inference system that it should spot that if p is decidable then func p is too.
Nima (Apr 09 2018 at 00:18):
Great, thanks
Kevin Buzzard (Apr 09 2018 at 00:21):
As for the equation compiler question (why it's not smart enough to make deductions about h) -- I'm afraid that's beyond my pay grade. You can see in your begin/end attempt that by the time we've got to the right of the colon-equals, Lean doesn't even seem to know that i is emt -- even if you explicitly write that it is -- so it can't make any deductions about h.
Simon Hudon (Apr 09 2018 at 00:27):
I think that when you match on both i
and h
, it allows Lean to change the type of h
(or rather, its patterns) to reflect that you're matching on i
. In the first branch, h : is_empty (val n)
and in the second, h : is_empty emt
. After that, we have definitional equality with false
(resp. true
) by simply unfolding is_empty
Simon Hudon (Apr 09 2018 at 00:28):
With regards to decidable, when a function returns true
or false
and then that you need it to be decidable, you can use bool
instead and it will be automatically decidable because there is an implicit conversion from bool
to Prop
and the resulting Prop
is decidable.
Nima (Apr 09 2018 at 00:33):
How do I prove decidable true
?
Regarding using bool
, it works, but then either I have write p=tt
or I will see the left symbol ↥p
everywhere (not sure how comfortable I am with that, or how much trouble it is going to cause me later).
Kevin Buzzard (Apr 09 2018 at 00:35):
decidable true
has type Type
so I'm not sure you can prove it.
Kevin Buzzard (Apr 09 2018 at 00:35):
Here's the definition of decidable -- it's an inductive type.
Kevin Buzzard (Apr 09 2018 at 00:36):
class inductive decidable (p : Prop) | is_false (h : ¬p) : decidable | is_true (h : p) : decidable
Kevin Buzzard (Apr 09 2018 at 00:36):
So you have two constructors, is_true
and is_false
Kevin Buzzard (Apr 09 2018 at 00:37):
#check is_true trivial -- decidable true
Kevin Buzzard (Apr 09 2018 at 00:38):
true
is a Prop and trivial
is a proof of true
Nima (Apr 09 2018 at 00:39):
So if output of a function is either true
or false
, how do I say this output is decidable (without going to bool
)? From what you just said, what I am trying to write in lean
does not even make sense. Right?
def func(p:Prop)[decidable p] : Prop := true example (p:Prop)[decidable p] : if (func p) then true else false := sorry -- won't type check
Kevin Buzzard (Apr 09 2018 at 00:41):
Well you have to prove that the output is either true or false, and then you can make an instance of the decidable class by using is_false if it's false and is_true if it's true
Kevin Buzzard (Apr 09 2018 at 00:43):
Take a look at how core Lean proves that less than or equal to is decidable on nat
Kevin Buzzard (Apr 09 2018 at 00:43):
instance decidable_le : ∀ a b : ℕ, decidable (a ≤ b) | 0 b := is_true (zero_le b) | (a+1) 0 := is_false (not_succ_le_zero a) | (a+1) (b+1) := match decidable_le a b with | is_true h := is_true (succ_le_succ h) | is_false h := is_false (λ a, h (le_of_succ_le_succ a)) end
Kevin Buzzard (Apr 09 2018 at 00:44):
Given the statement a <= b
it either proves that it's true or proves that it's false, and in each case it creates an instance of decidable (a <= b)
Kevin Buzzard (Apr 09 2018 at 00:45):
using the is_true
or is_false
constructors
Kevin Buzzard (Apr 09 2018 at 00:46):
things like nat.zero_le
are lemmas in Lean (that one says "forall n, 0 <= n")
Kevin Buzzard (Apr 09 2018 at 00:46):
Earlier in the file they must have written "open nat" so you don't have to keep writing "nat."
Nima (Apr 09 2018 at 00:50):
Sorry, but I still cannot figure out how to make this example work without going to bool
def func(p:Prop) : Prop := true instance func_of_decidable_is_decidable (p : Prop) : decidable (func p) := begin simp [func], -- I have to prove decidable true admit end example (p:Prop) : if (func p) then true else false := sorry
Kevin Buzzard (Apr 09 2018 at 00:53):
def func(p:Prop) : Prop := true instance func_of_decidable_is_decidable (p : Prop) : decidable (func p) := is_true trivial
Kevin Buzzard (Apr 09 2018 at 00:53):
I just use the constructor
Kevin Buzzard (Apr 09 2018 at 00:54):
in tactic mode you can write exact is_true trivial
Nima (Apr 09 2018 at 00:54):
Thanks, now I got it!
Kevin Buzzard (Apr 09 2018 at 00:54):
The only way you can ever prove decidable anything is to use a constructor. decidable is an inductive type with two constructors and the only way to make an instance of it is to use one of the constructors.
Mario Carneiro (Apr 09 2018 at 00:59):
decidable.true
proves decidable true
Mario Carneiro (Apr 09 2018 at 01:02):
instance (i) : decidable (is_nonempty i) := by cases i; unfold is_nonempty; apply_instance
Mario Carneiro (Apr 09 2018 at 01:03):
valof
can also be defined without the emt
case at all:
def valof : Π (i : ind), is_nonempty i → ℕ | (val n) h := n
This is because lean does cases on h
there and needs no cases since it's false
Mario Carneiro (Apr 09 2018 at 01:04):
I don't recommend using bool
in this case if you can help it; it's possible but the coercions will get in your way sooner or later
Nima (Apr 09 2018 at 01:06):
what is the difference between unfold
and rw
?
Mario Carneiro (Apr 09 2018 at 01:06):
Also, you can prove decidable T
for any expression T
that is already known to be decidable from earlier stuff (like ands of ors of true and natural number equality and other things like that) by apply_instance
.
Mario Carneiro (Apr 09 2018 at 01:07):
unfold
does definitional unfolding; the syntax is unfold X
where X
is a definition will rewrite with the equation lemmas for X
. rw
does arbitrary (non-definitional) rewriting with any equations you give it
Mario Carneiro (Apr 09 2018 at 01:08):
However, rw X
also works as a shorthand for "rewrite with the equation lemmas for X
" which makes it very similar to unfold
in this instance
Nima (Apr 09 2018 at 01:28):
Thank you,
After all these helps and more than an hour I wrote 6 lines as a proof :scream:
Mario Carneiro (Apr 09 2018 at 01:31):
"perfection is when there is nothing left to take away" - Antoine de Saint-Exupery
Nima (Apr 09 2018 at 01:36):
Just to be clear, that was just survival; no perfection was involved!
Simon Hudon (Apr 09 2018 at 01:37):
I also like to quote Adventure Time when it comes to learning functional programming and proving: sucking is the first step towards being kind of good at something.
Simon Hudon (Apr 09 2018 at 01:41):
The fun thing about Haskell and Lean is that ugly code is still very much usable. As you learn more, you see it improve over time. My ugly Haskell and Lean code is still easier to refactor and evolve than anything I wrote in any language. Just to say: starting with bad code is not too much of a problem
Nima (Apr 09 2018 at 02:59):
When I use cases
on an inductive type, how can I specify the first case that I would like to consider?
When I am in tactic mode, how can I have pattern matching on two inductive types at the same time?
Kenny Lau (Apr 09 2018 at 03:00):
nested cases?
Kenny Lau (Apr 09 2018 at 03:00):
When I use
cases
on an inductive type, how can I specify the first case that I would like to consider?
in tactic mode you can write
case list.nil { simp }, case list.cons { simp }
Nima (Apr 09 2018 at 03:04):
Thanks for case
By "nested cases" you mean writing multiple cases
?
I meant something like match a,b with
? Is there anything like that available?
Kenny Lau (Apr 09 2018 at 03:05):
exact match a,b with
Kenny Lau (Apr 09 2018 at 03:05):
you can always use exact
to go into term mode
Kenny Lau (Apr 09 2018 at 03:05):
and by
to go into tactic mode
Kenny Lau (Apr 09 2018 at 03:05):
no, I don't think there's a tactic for nested cases
Nima (Apr 09 2018 at 03:06):
exact match a,b with
is exactly what I was looking for. Thank you.
Mario Carneiro (Apr 09 2018 at 04:05):
rcases
, from the mathlib tactics, does multiple cases
Mario Carneiro (Apr 09 2018 at 04:06):
but it only works with one input expression. In fact regular cases will automatically generalize dependent hypotheses like match i, h with
in your earlier example.
Nima (Apr 10 2018 at 01:11):
Does it make any sense if I use classical
to prove something like decidable (number.choose α = none)
?
Mario Carneiro (Apr 10 2018 at 01:41):
You can, but option.is_none
is already decidable
Simon Hudon (Apr 10 2018 at 02:05):
There's also something contradictory to using classical reasoning to prove decidability: decidability is preferable to classical.em
(excluded middle) especially to preserve computability. If you bring in classical reasoning, you lose computability so you might as well go all the way and just use classical.prop_decidable
to make every proposition decidable:
local attribute [instance] classical.prop_decidable
This is useful to use if _ then _ else _
without proving decidability.
Nima (Apr 10 2018 at 02:17):
So if I want to use decidable
as what it is intended to be then I should not use classical
. Right?
Kenny Lau (Apr 10 2018 at 02:20):
but classical.prop_decidable
just says that every prop
is decidable
Mario Carneiro (Apr 10 2018 at 02:21):
decidable
is a proof obligation that is trivial if you are being classical. Most conventional math in lean is classical, so you usually don't have to worry about this. You should think about decidability if you are writing an executable program (if you have to mark your program noncomputable
it won't run).
Mario Carneiro (Apr 10 2018 at 02:22):
You can attempt to avoid LEM even when proving theorems, like Kenny, but the library won't really help you in this quest so it's an uphill battle
Nima (Apr 10 2018 at 02:23):
Please look at the last two lines, as I am not sure if anything else there is relevant
α : Type u_1, _inst_1 : number α, c : constraint α, d₁ : decidable (is_trivial c), d₂ : decidable_linear_order α, _match : Π (_a : decidable (is_trivial c)), decidable (is_satisfiable c), hnt : ¬is_trivial c, d₃ : decidable (is_lower_bound c), _match : decidable (is_lower_bound c) → decidable (ite (is_lower_bound c) (is_satisfiable._match_3 c hnt (number.max α)) (is_satisfiable._match_4 c hnt (number.min α))), hl : is_lower_bound c, _match : Π (_a : option α), decidable (is_satisfiable._match_3 c hnt _a), m : α, aa : decidable (get_bound c hnt < m) ⊢ decidable (get_bound c hnt < m)
If I do exact aa,
I receive this cryptic error message.
invalid type ascription, term has type decidable (@has_lt.lt α (@preorder.to_has_lt α (@partial_order.to_preorder α (@linear_order.to_partial_order α (@linear_order.mk α (@decidable_linear_order.le α d₂) (@decidable_linear_order.lt α d₂) _ _ _ _ _)))) (@constraint.get_bound α _inst_1 c hnt) m) but is expected to have type decidable (@has_lt.lt α (@preorder.to_has_lt α (@partial_order.to_preorder α (@linear_order.to_partial_order α (@number.number.to_linear_order α _inst_1)))) (@constraint.get_bound α _inst_1 c hnt) m) state: α : Type u_1, ...
I know I might not gave enough information to receive an exact answer, but is there any clue you can share about why this might happen?
Mario Carneiro (Apr 10 2018 at 02:24):
You have decidable (is_trivial c)
as an assumption, which is probably a bad sign. How is is_trivial
and constraint
defined?
Kenny Lau (Apr 10 2018 at 02:25):
diamond of death?
Simon Hudon (Apr 10 2018 at 02:26):
I think decidable_linear_order α
is more problematic because it is already subsumed by number α
Mario Carneiro (Apr 10 2018 at 02:26):
yes, number α
and decidable_linear_order α
both supply a less-equal relation
Mario Carneiro (Apr 10 2018 at 02:27):
you should replace decidable_linear_order α
with @decidable_rel α (<)
Nima (Apr 10 2018 at 02:27):
def is_trivial : constraint α → Prop | (cnstr ktrv _ _ _ _) := true | (cnstr (kdyn trv _) _ _ _ _) := true | _ := false
Mario Carneiro (Apr 10 2018 at 02:27):
or define decidable_number
Mario Carneiro (Apr 10 2018 at 02:28):
You should be able to prove decidable (is_trivial c)
for all c
as an instance, so it shouldn't need to be an assumption
Nima (Apr 10 2018 at 02:29):
I make no claim of writing in the best or even a good way. Just trying to find my way through.
inductive triviality | trv | ntr inductive direction | lower | upper inductive strictness | stt | nst inductive triviality_kind {α:Type*} | ktrv : triviality_kind | kntr (bnd : α) : triviality_kind | kdyn (tr : triviality) (bnd : α) : triviality_kind inductive direction_kind | none | klower | kupper | kdynam (dir : direction) inductive strictness_kind | none | kstt | knst | kdyn (st : strictness) structure constraint (α : Type*) := cnstr :: (trvk : @triviality_kind α) (dirk : direction_kind ) (strk : strictness_kind) (ndir : dirk = direction_kind.none ↔ trvk = triviality_kind.ktrv) (nstr : strk = strictness_kind.none ↔ trvk = triviality_kind.ktrv)
instance is_trivial_is_decidable (c : constraint α) : decidable (is_trivial c) := begin cases c; cases trvk; try { unfold is_trivial }; try { cases tr }; try { exact decidable.true }; try { exact decidable.false}, end
Mario Carneiro (Apr 10 2018 at 02:30):
You seem to already have the proof I mention there
Mario Carneiro (Apr 10 2018 at 02:32):
What produced the d₁ : decidable (is_trivial c)
in the theorem you are proving?
Nima (Apr 10 2018 at 02:33):
I am trying to prove the following is decidable
def is_satisfiable (c : constraint α) [d : decidable (is_trivial c)] : Prop := match d with | is_true _ := number.choose α ≠ none | is_false ht := match get_strictness c ht with | nst := true | stt := if is_lower_bound c then match number.max α with | none := true | some m := get_bound c ht < m end else match number.min α with | none := true | some m := m < get_bound c ht end end end
Second line:
instance is_satisfiable_is_decidable (c : constraint α) [d₁ : decidable (is_trivial c)] [d₂ : decidable_linear_order α] : decidable (is_satisfiable c) := match d₁ with | is_true ht := by unfold is_satisfiable; simp; apply_instance | is_false hnt := begin unfold is_satisfiable, cases get_strictness c hnt; unfold is_satisfiable._match_2, begin have d₃ : decidable (is_lower_bound c), by apply_instance, exact match d₃ with | is_true hl := begin simp [hl], exact match number.max α with | none := by unfold is_satisfiable._match_3; exact decidable.true | some m := begin unfold is_satisfiable._match_3, have aa := decidable_linear_order.decidable_lt α (get_bound c hnt) m, exact aa, end -- by unfold is_satisfiable._match_3; apply_instance end end | is_false hnl := begin simp [hnl], admit end end, end, exact decidable.true end end
Mario Carneiro (Apr 10 2018 at 02:33):
drop the line [d₁ : decidable (is_trivial c)]
Mario Carneiro (Apr 10 2018 at 02:33):
You can do the match by writing if ht : is_trivial c then ... else ...
Nima (Apr 10 2018 at 02:38):
Should I drop [d : decidable (is_trivial c)]
from definition of is_satisfiable
as well?
Mario Carneiro (Apr 10 2018 at 02:38):
Given the complexity of your definition, I think you would do better to change is_satisfiable
to type def is_satisfiable (c : constraint α) : bool :=
. Same for is_trivial
, since it's just a bunch of true
and false
branches
Mario Carneiro (Apr 10 2018 at 02:39):
Then decidability will be trivial, and you can prove the unfolding of the individual branches as (much easier) lemmas
Mario Carneiro (Apr 10 2018 at 02:46):
I don't have all your definitions so I don't know if this typechecks, but something like this:
def is_trivial : constraint α → bool | (cnstr ktrv _ _ _ _) := tt | (cnstr (kdyn trv _) _ _ _ _) := tt | _ := ff def is_satisfiable (c : constraint α) : bool := if ht : is_trivial c then (number.choose α).is_some else match get_strictness c ht with | nst := tt | stt := if is_lower_bound c then match number.max α with | none := tt | some m := get_bound c ht < m end else match number.min α with | none := tt | some m := m < get_bound c ht end end
Nima (Apr 10 2018 at 02:55):
Would you please tell me why I should have dropped [d₁ : decidable (is_trivial c)]
?
Shouldn't I have one of these for every property I put in if then else
?
Simon Hudon (Apr 10 2018 at 02:57):
When you look at (get_bound c hnt < m)
you should see that <
is generic, i.e. it is inferred from instances. Because of that you should see that it takes four parameters, not two:
α
- some instance of
has_lt α
get_bound c hnt
m
This means that if different instances of has_lt
are inferred for two <
propositions, they are not syntactically the same so you can't use one to prove the other
Simon Hudon (Apr 10 2018 at 02:58):
That means that you have to consciously try to have a unique path to any instance that you're going to use. If the instances are conceptually the same, it might still be hard to prove and the effort is usually not worth it
Mario Carneiro (Apr 10 2018 at 03:03):
When you say [d₁ : decidable (is_trivial c)]
, you are defining a function parametric over proofs that triviality is decidable. There's no need to do this, you already have such a proof/function and want to call it whenever you want to decide if a constraint is trivial
Mario Carneiro (Apr 10 2018 at 03:05):
If you say if ht : is_trivial c then ...
, it automatically infers the decidable (is_trivial c)
argument (using the environment, not just the local context), which is what you want.
Nima (Apr 10 2018 at 03:11):
Thank you both,
I need time to take these in ...
Simon Hudon (Apr 10 2018 at 03:19):
Sure :) it's a slow process. You can have Lean's pretty printer show more information and, among others, show the implicit and type class arguments by using set_option pp.all true
before your theorem.
Nima (Apr 10 2018 at 04:43):
OK, after I changed something and I will get back to it later, I managed to rewrote the whole proof in the following nicer way.
As you can see something is almost duplicated. Is there anyway I can make this even shorter (ie remove those duplications)?
instance is_satisfiable_is_decidable (c : constraint α) : decidable (is_satisfiable c) := if ht : is_trivial c then by { unfold is_satisfiable, simp [ht], apply_instance } else if hs : is_nonstrict c then by { unfold is_satisfiable, simp [hs], apply_instance } else if hl : is_lower_bound c then by unfold is_satisfiable; cases number.max α with m; simp [ht,hs,hl,is_satisfiable]; apply_instance else by unfold is_satisfiable; cases number.min α with m; simp [ht,hs,hl,is_satisfiable]; apply_instance
Simon Hudon (Apr 10 2018 at 04:49):
Can you reproduce the definitions of is_satisfiable
, constraint
, is_trivial
, is_nonstrict
and is_lower_bound
please?
Nima (Apr 10 2018 at 04:52):
I did not mean shorter proof using all these definitions. I thought just because those lines look a lot like each other, one should be able to merge them using something like try
. But if I write if ht : is_trivial c ∨ is_nonstrict c then
then I cannot even write cases ht
, and I did not understand why.
structure constraint (α : Type*) := cnstr :: (trvk : @triviality_kind α) (dirk : direction_kind ) (strk : strictness_kind) (ndir : dirk = direction_kind.none ↔ trvk = triviality_kind.ktrv) (nstr : strk = strictness_kind.none ↔ trvk = triviality_kind.ktrv) def is_trivial : constraint α → Prop | (cnstr ktrv _ _ _ _) := true | (cnstr (kdyn trv _) _ _ _ _) := true | _ := false def is_lower_bound : constraint α → Prop | (cnstr _ klower _ _ _) := true | (cnstr _ (kdynam lower) _ _ _) := true | _ := false def is_nonstrict : constraint α → Prop | (cnstr _ _ knst _ _) := true | (cnstr _ _ (kdyn nst) _ _) := true | _ := false def is_satisfiable (c : constraint α) : Prop := if ht : is_trivial c then number.choose α ≠ none else if is_nonstrict c then true else if is_lower_bound c then match number.max α with | none := true | some m := get_bound c ht < m end else match number.min α with | none := true | some m := m < get_bound c ht end
Simon Hudon (Apr 10 2018 at 04:59):
I'd like to replace your big if _ then _ else _
with a match
but I can't think of a nice way to do it. Instead, let's try this:
begin refine ( if ht : is_trivial c then _ else if is_nonstrict c then _ else if is_lower_bound c then match number.max α with | none := _ | some m := _ end else match number.min α with | none := _ | some m := _ end ) ; simp [*, is_satisfiable] ; apply_instance end
Simon Hudon (Apr 10 2018 at 04:59):
Not very concise but less repetitive
Simon Hudon (Apr 10 2018 at 05:04):
As a side note: I suggest you pick more obvious names -- that is, names whose meaning is obvious rather than names that are easy to pick -- that names such as knst
. I feel overly short names pose a puzzle to the reader.
Nima (Apr 10 2018 at 05:07):
Thanks a lot, seems something I can use often.
Regarding refine
, I found this:
The refine tactic applies the expression in question to the goal, but leaves any remaining metavariables for us to fill
Is that what you are trying to do?
How does refine
knows inside (..)
is supposed to be definition of is_satisfiable
and so it can fill all the _
?
Nima (Apr 10 2018 at 05:11):
As a side note: I suggest you pick more ...
Sometimes I think I might be sick, since I prefer stt
and nst
over strict
and non_strict
, mainly because when you write them in two consecutive lines, they are nicely aligned! :upside_down_face:
Simon Hudon (Apr 10 2018 at 05:14):
Yes that's what I'm trying to do. Think of refine as applying type inference to (if ... then _ else ... : decidable (is_satisfiable c))
. Before we start, we don't know the type of _
but then, we use the type of dite
(the if _ then _ else _
function) and we can figure out that _
has type decidable (is_satisfiable c)
. When we're done with type inference / type checking, we still don't know a term (or proof) to assign to _
so building such a term becomes the goal of a subproof.
Simon Hudon (Apr 10 2018 at 05:15):
Symbols that line up do look nice! As a compromise, you may consider padding with spaces
Nima (Apr 10 2018 at 05:19):
OK, I copy-pasted your suggestion and I am receiving the following error (highlighted below the first ;
), is this a version issue? (I am on 3.3.0)
Tactic State α : Type u_1, _inst_1 : number α, c : constraint α ⊢ decidable (is_satisfiable c) don't know how to synthesize placeholder context: α : Type u_1, _inst_1 : number α, c : constraint α, ht : ¬is_trivial c, _match : option α → decidable (is_satisfiable c) ⊢ decidable (is_satisfiable c) state: α : Type u_1, _inst_1 : number α, c : constraint α ⊢ decidable (is_satisfiable c)
Mario Carneiro (Apr 10 2018 at 05:21):
I don't think your approach will work, Simon. First of all, match
and let match
have a tendency to be insulated from pexpr metavariable generation, so that they end up being immediate (term) goals even if they appear in a refine
Mario Carneiro (Apr 10 2018 at 05:22):
Second, it is important for Nima's proof approach that the number.max α
argument be exposed at the point of the match
/cases
, otherwise it won't be generalized. That means that the unfold is_satisfiable
command has to come before the match attempt
Simon Hudon (Apr 10 2018 at 05:22):
@Mario Carneiro I think you're right.
@Nima Would you mind using https://gist.github.com/ to share all the definitions needed to make the example work?
Simon Hudon (Apr 10 2018 at 05:24):
I'll try to make a proof work on my side and get back to you
Nima (Apr 10 2018 at 05:51):
Here is a link
lets-learn-some-lean
Mario Carneiro (Apr 10 2018 at 05:53):
In number.lean
, you almost certainly want :
instead of :=
in the fields neg₀
etc
Nima (Apr 10 2018 at 05:55):
Sure, thanks,
Simon Hudon (Apr 10 2018 at 06:33):
Here's my next suggestion:
instance is_satisfiable_is_decidable (c : constraint α) : decidable (is_satisfiable c) := begin refine ( if ht : is_trivial c then _ else if hs : is_nonstrict c then _ else if hl : is_lower_bound c then _ else _ ) ; simp [*, is_satisfiable] ; try { generalize : number.max α = x, cases x, } ; try { generalize : number.min α = x, cases x, } ; try { simp [*, is_satisfiable] } ; apply_instance, end
Simon Hudon (Apr 10 2018 at 06:33):
It could work without the generalize
s but it would produce many more cases
Mario Carneiro (Apr 10 2018 at 06:34):
why is that?
Mario Carneiro (Apr 10 2018 at 06:34):
also isn't the first simp
redundant?
Simon Hudon (Apr 10 2018 at 06:34):
Yeah, I just noticed that
Mario Carneiro (Apr 10 2018 at 06:35):
also the parens around the refine
should be unnecessary
Simon Hudon (Apr 10 2018 at 06:35):
I mean: which first simp
?
Simon Hudon (Apr 10 2018 at 06:37):
also the parens around the
refine
should be unnecessary
I think it might create a precedence problem with ;
Simon Hudon (Apr 10 2018 at 06:41):
To answer your first question: it generates many more cases because we only need cases number.max α
(resp. cases number.min α
) in one branch out of four so, at the first try
, without the generalize
, we end up with 8
cases instead of 5
and, at the second try, we end up with 16
cases instead of 6
. We don't see all those cases but somehow knowing that they're there would give me nightmares
Mario Carneiro (Apr 10 2018 at 06:42):
does generalize
fail if the target expression is not present?
Simon Hudon (Apr 10 2018 at 06:44):
generalize : number.max α = x
does but not generalize h : number.max α = x
Simon Hudon (Apr 10 2018 at 06:48):
I kind of like this use of refine
. Maybe a tactic such as if_then_else [is_trivial c,is_nonstrict c,is_lower_bound c]
would be nicer though.
Mario Carneiro (Apr 10 2018 at 06:49):
A remark on theorems like this, where you have to go through all the cases in your original definition: You have to do this every time you want to prove anything about is_satisfiable
, which is why I prefer to write a custom recursor that does the match splits for you. You can then use it to define is_satisfiable
itself, as well as theorems proving properties about it
Simon Hudon (Apr 10 2018 at 06:49):
That make sense. Do you also write a tactic to apply the recursor?
Mario Carneiro (Apr 10 2018 at 06:50):
by_cases
is the tactic version of if then else
Simon Hudon (Apr 10 2018 at 06:50):
Can you give it multiple conditions?
Mario Carneiro (Apr 10 2018 at 06:50):
Not usually, I will either write it explicitly as a term or use apply is_satisfiable.rec
Mario Carneiro (Apr 10 2018 at 06:50):
no
Mario Carneiro (Apr 10 2018 at 06:51):
I'm not convinced you will always have this structure anyway; it seems like it's best just to nest the by_cases
applications
Mario Carneiro (Apr 10 2018 at 06:52):
you could also have if h1 then if h2 then e1 else e2 else e3
Nima (Apr 10 2018 at 06:58):
Thank you for the solution.
By actually doing it, I can see cases number.max α
creates a lot more cases than generalize : number.max α = x, cases x
. But why (aren't they both cases on none
and some _
)?
Simon Hudon (Apr 10 2018 at 07:00):
In generalize : number.max α = x, cases x
, generalize : number.max α = x
fails if number.max α
is not used in the goal and therefore cases x
doesn't get executed. It's a complicated way of saying cases number.max α
but only if I actually use number.max α
Nima (Apr 10 2018 at 07:03):
I see,
Also, is this recuroser a concept in lean or Haskell?
... which is why I prefer to write a custom recursor that does the match splits for you. ...
Theorem Proving in Lean, Section 7.1
... It is also known as a recursor, and it is what makes the type “inductive” ...
??
Simon Hudon (Apr 10 2018 at 07:03):
By the time it is used, number.max α
has disappeared from all but one goal
Simon Hudon (Apr 10 2018 at 07:06):
The recursor is a Lean and dependent type theory concept
Simon Hudon (Apr 10 2018 at 07:09):
In Lean, an inductive type definition is not a first class citizen. It is translated into a bunch of constants and definitions.
Simon Hudon (Apr 10 2018 at 07:09):
Let's take a look at:
inductive my_option (α : Type*) | none : my_option | some : α → my_option
Sean Leather (Apr 10 2018 at 07:09):
It's not limited to dependent types, of course. :simple_smile:
Simon Hudon (Apr 10 2018 at 07:10):
Thanks for the precision!
Sean Leather (Apr 10 2018 at 07:11):
Gotta keep you on your toes. :wink:
Simon Hudon (Apr 10 2018 at 07:11):
If we type in
#print prefix my_option
we see the constants and definitions that my_option
is translated into
Simon Hudon (Apr 10 2018 at 07:17):
Sean will correct me if I'm wrong but I think only three of them are constants: two constructors
my_option.none : Π (α : Type u_1), my_option α my_option.some : Π {α : Type u_1}, α → my_option α
and one recursor
my_option.cases_on : Π {α : Type u_1} {C : my_option α → Sort l} (n : my_option α), C (my_option.none α) → (Π (a : α), C (my_option.some a)) → C n
(I just realized that no_confusion
is actually a definition)
Simon Hudon (Apr 10 2018 at 07:19):
The recursor basically tells you how to pattern match on my_option
. When you pattern match on opt : my_option α
, you're constructing a value of C opt
(for some C
) and you have to provide a way to construct C opt
in the case where opt
is none
and in the case where opt
is some a
Mario Carneiro (Apr 10 2018 at 07:20):
Actually the constant is my_option.rec_on
, and of course my_option
itself is also a constant
Gabriel Ebner (Apr 10 2018 at 07:21):
rec_on
should be a definition as well. rec
is the recursor constant.
Simon Hudon (Apr 10 2018 at 07:21):
Aren't rec_on
and cases_on
defined in term of each other?
Mario Carneiro (Apr 10 2018 at 07:21):
rec_on
and cases_on
are the same for a nonrecursive inductive type
Simon Hudon (Apr 10 2018 at 07:23):
What I mean is, for recursive inductive types, do we actually have two separate constants for cases
and rec
?
Mario Carneiro (Apr 10 2018 at 07:25):
there is no cases
for some reason, just cases_on
, and no, rec
is the only elimination-like constant, cases_on
just ignores the inductive hypotheses
Kevin Buzzard (Apr 10 2018 at 07:26):
@Simon Hudon https://xenaproject.wordpress.com/2018/03/24/no-confusion-over-no_confusion/ -- no_confusion
is defined in terms of the eliminator, but I always found the definition rather obscure. I wrote some notes about it in the link.
Simon Hudon (Apr 10 2018 at 07:26):
Ok, that's what I thought. So I cut some cornets in my explanation by making it about cases_on
instead of rec
Simon Hudon (Apr 10 2018 at 07:27):
@Kevin Buzzard Thanks! I was sure it had to be postulated too. I guess i got confused :stuck_out_tongue_closed_eyes:
Sean Leather (Apr 10 2018 at 07:28):
Just for the sake of completeness and posterity, is this what everybody is seeing after #print prefix my_option
?
my_option : Type u_1 → Type u_1 my_option.cases_on : Π {α : Type u_1} {C : my_option α → Sort l} (n : my_option α), C (my_option.none α) → (Π (a : α), C (my_option.some a)) → C n my_option.has_sizeof_inst : Π (α : Type u_1) [α_inst : has_sizeof α], has_sizeof (my_option α) my_option.no_confusion : Π {α : Type u_1} {P : Sort l} {v1 v2 : my_option α}, v1 = v2 → my_option.no_confusion_type P v1 v2 my_option.no_confusion_type : Π {α : Type u_1}, Sort l → my_option α → my_option α → Sort l my_option.none : Π (α : Type u_1), my_option α my_option.none.inj : ∀ {α : Type u_1}, my_option.none α = my_option.none α → true my_option.none.inj_arrow : Π {α : Type u_1}, my_option.none α = my_option.none α → Π ⦃P : Sort l⦄, (true → P) → P my_option.none.inj_eq : ∀ {α : Type u_1}, my_option.none α = my_option.none α = true my_option.none.sizeof_spec : ∀ (α : Type u_1) [α_inst : has_sizeof α], my_option.sizeof α (my_option.none α) = 1 my_option.rec : Π {α : Type u_1} {C : my_option α → Sort l}, C (my_option.none α) → (Π (a : α), C (my_option.some a)) → Π (n : my_option α), C n my_option.rec_on : Π {α : Type u_1} {C : my_option α → Sort l} (n : my_option α), C (my_option.none α) → (Π (a : α), C (my_option.some a)) → C n my_option.sizeof : Π (α : Type u_1) [α_inst : has_sizeof α], my_option α → ℕ my_option.some : Π {α : Type u_1}, α → my_option α my_option.some.inj : ∀ {α : Type u_1} {a a_1 : α}, my_option.some a = my_option.some a_1 → a = a_1 my_option.some.inj_arrow : Π {α : Type u_1} {a a_1 : α}, my_option.some a = my_option.some a_1 → Π ⦃P : Sort l⦄, (a = a_1 → P) → P my_option.some.inj_eq : ∀ {α : Type u_1} {a a_1 : α}, my_option.some a = my_option.some a_1 = (a = a_1) my_option.some.sizeof_spec : ∀ (α : Type u_1) [α_inst : has_sizeof α] (a : α), my_option.sizeof α (my_option.some a) = 1 + sizeof a
Simon Hudon (Apr 10 2018 at 07:29):
Yes
Sean Leather (Apr 10 2018 at 07:29):
Awesome. I haven't upgraded Lean in a while, so some things may have changed. :simple_smile:
Simon Hudon (Apr 10 2018 at 07:29):
Or rather, until other people answer: I don't know
Kevin Buzzard (Apr 10 2018 at 07:29):
@Simon Hudon here's an indication that it doesn't have to be postulated -- once you have cases
for nat
you can define is_zero
by is_zero 0 = tt
and is_zero (succ n) = ff
and then prove that zero can't be a successor that way.
Kevin Buzzard (Apr 10 2018 at 07:30):
Is this what _everybody_ is seeing??
Kevin Buzzard (Apr 10 2018 at 07:30):
I don't know either.
Sean Leather (Apr 10 2018 at 07:30):
One response was enough for me, it seems.
Kevin Buzzard (Apr 10 2018 at 07:30):
[i.e. it's what I am seeing]
Kenny Lau (Apr 10 2018 at 07:30):
Is this what _everybody_ is seeing??
we all know you teach m1f
Simon Hudon (Apr 10 2018 at 07:31):
I don't. What is m1f?
Sean Leather (Apr 10 2018 at 07:31):
I only required an extistential result, rather than a universal quantification.
Kevin Buzzard (Apr 10 2018 at 07:31):
m1f = introduction to proof course
Kenny Lau (Apr 10 2018 at 07:32):
@Kevin Buzzard one should probably change the course name then
Simon Hudon (Apr 10 2018 at 07:32):
So let's rephrase then: does there exist a participant other than myself that also sees the following output ... ?
Kevin Buzzard (Apr 10 2018 at 07:32):
Yes.
Nima (Apr 10 2018 at 07:32):
Yes
Sean Leather (Apr 10 2018 at 07:34):
@Kenny Lau I think it's not only the dictionary that uses English to describe English. We also do that here.
Kenny Lau (Apr 10 2018 at 07:35):
doom day's clock is ticking
Sean Leather (Apr 10 2018 at 07:36):
@Nima I'm just curious what your background is relevant to Lean. Have you worked with functional programming or proof assistants?
Simon Hudon (Apr 10 2018 at 07:40):
Alright! Enough excitement for today. I have to wake up in a few hours and get back to writing. Good <<insert current period of the day of your timezone here>> everyone!
Sean Leather (Apr 10 2018 at 07:41):
And good night to you, @Simon Hudon!
Moses Schönfinkel (Apr 10 2018 at 07:41):
Good night. @Sean Leather has returned too, neat! Hello.
Kevin Buzzard (Apr 10 2018 at 07:41):
You can check time zones on Zulip I noticed.
Kenny Lau (Apr 10 2018 at 07:41):
interesting
Sean Leather (Apr 10 2018 at 07:41):
@Moses Schönfinkel Hello, hello! You don't have any news on Lean 5 to tell me about, do you? :worried:
Kevin Buzzard (Apr 10 2018 at 07:41):
click on the down-arrow which appears when you mouse over a person's name on the right
Kevin Buzzard (Apr 10 2018 at 07:42):
and you see their local time
Moses Schönfinkel (Apr 10 2018 at 07:42):
I have been having some issues with my crystal ball ever since my cat cracked it.
Kevin Buzzard (Apr 10 2018 at 07:42):
although Kenny went from Lon to HK and didn't update his
Nima (Apr 10 2018 at 07:42):
functional programming:
Only Scala, which based on what I see, I would not be surprised if people find it insulting that I put Scala and FP side by side ;)
proof assistants
I worked with PVS, for example, I have a paper in which I said something is wrong and this is the right version. In order to be more confident, I proved some of the stuff in PVS (about 25K proof commands, which of course could be very inefficient proofs)
Kenny Lau (Apr 10 2018 at 07:43):
although Kenny went from Lon to HK and didn't update his
done
Moses Schönfinkel (Apr 10 2018 at 07:44):
Scala is the best way to target JVM.
Simon Hudon (Apr 10 2018 at 07:46):
@Nima Would you mind giving us a reference to your paper? I did some PVS too and I'd like to see what you did
Nima (Apr 10 2018 at 07:50):
Revisiting MITL to Fix Decision Procedures
https://link.springer.com/chapter/10.1007/978-3-319-73721-8_22
Proofs can be found here (link is also in the paper)
http://uofi.box.com/v/PVSProofsOfMITL
Simon Hudon (Apr 10 2018 at 07:50):
Thanks!
Simon Hudon (Apr 10 2018 at 07:50):
Ok, now I'm really off
Nima (Apr 10 2018 at 19:26):
I have a type constraint
which represents something like bnd < x
or x < bnd
.
I have a function sem
(semantics) that receives a constraint as input and return a predicate one ℕ
as output (the set of values satisfied by the constraint). Let c
be a constraint. I would like to be able to write x : c
. For that, I have the following code, but I am not sure about {x // sem S x}
. Is there a mistake or a better way there?
open nat inductive constraint | low (bnd: ℕ) | upp (bnd: ℕ) open constraint def sem (c : constraint) (a : ℕ) : Prop := match c with | low bnd := bnd < a | upp bnd := a < bnd end instance constraint_to_pred : has_coe (constraint) (ℕ → Prop) := ⟨λ c, λ a, sem c a⟩ instance constraint_to_sort: has_coe_to_sort (constraint ) := {S := Type*, coe := λ S, {x // sem S x}}
If everything is fine, how do I finish the following example:
example : ∀ a : (low 1), a.val > 1 := begin intro a, admit, end
Simon Hudon (Apr 10 2018 at 19:30):
It looks good to me except that I don't know that you actually need the has_coe
instance
Simon Hudon (Apr 10 2018 at 19:30):
For the proof, you can try:
example : ∀ a : (low 1), a.val > 1 := begin unfold_coes, intro a, admit, end
Simon Hudon (Apr 10 2018 at 19:31):
It should unfold all the coercions and then you should see a clear way to finishing the proof
Nima (Apr 10 2018 at 19:35):
If I comment the first instance then the second one won't type check (may be because of {x // sem S x}
that I prefer not to have any way)
type mismatch at field 'coe' λ (S : constraint), {x // ⁇} has type constraint → Sort (max 1 ?) : Type (max 1 ?) but is expected to have type constraint → Type ? : Type (?+1)
If I don't comment the first instance then I will receive the following error: unknown identifier 'unfold_coes'
Simon Hudon (Apr 10 2018 at 19:36):
You need import tactic
Simon Hudon (Apr 10 2018 at 19:39):
If you want to remove the first instance, I think you need to change the second to:
instance constraint_to_sort: has_coe_to_sort (constraint ) := {S := Sort*, coe := λ S, {x // sem S x}}
Nima (Apr 10 2018 at 19:46):
OK, I don't know what happened, but removing the first instance does not give me error any more
You need import tactic
Do you mean open tactic
? Line import init.meta.tactic
will type check, but import tactic
will not.
Simon Hudon (Apr 10 2018 at 19:47):
You're not using leanpkg
and mathlib
right? Now would be a good time to bring them in
Simon Hudon (Apr 10 2018 at 19:48):
In a terminal where your sources are, type in:
leanpkg init lets-learn-some-lean leanpkg add leanprover/mathlib
Nima (Apr 10 2018 at 20:42):
OK, I have imported tactic. But I still receive the same error.
import tactic import tactic.interactive open nat inductive constraint | low (bnd: ℕ) | upp (bnd: ℕ) open constraint def sem (c : constraint) (a : ℕ) : Prop := match c with | low bnd := bnd < a | upp bnd := a < bnd end -- this instance seems redundant for now instance constraint_to_pred : has_coe (constraint) (ℕ → Prop) := ⟨λ c, λ a, sem c a⟩ instance constraint_to_sort: has_coe_to_sort (constraint ) := {S := Type*, coe := λ S, {x // sem S x}} example : ∀ a : (low 1), a.val > 1 := begin unfold_coes, -- ERROR: unknown identifier 'unfold_coes' intro a, admit, end
I think dependency is successfully created. For example, I can make the following work perfectly fine.
variables A B C D : Prop example (H : A) (H₁ : ¬ B) : ¬ (A → B) := by finish
But no luck with unfold_coes
Nima (Apr 12 2018 at 06:01):
Inside a proof, I have two cases. In case 1, I prove something about lower bound and in case 2, I prove something about upper bound. Each case has multiple steps, but the structure is identical (in order to obtain the second part from the first part, all I need to do is replace word mid
with max
).
How do I write both these cases without practically writing a part of the proof twice?
Kenny Lau (Apr 12 2018 at 06:02):
where is the word mid
and max
?
Kenny Lau (Apr 12 2018 at 06:02):
could you post your code?
Nima (Apr 12 2018 at 06:05):
The whole code is too long, but here is the part I was talking about (I just wanted to see what the general approach is in these situations. Do you create a whole new meta
function, or you can do something inline):
by_cases is_lower_bound c with hl ; simp [hl], begin have hl' := iff.mp (is_lower_bound_is_lower c ht) hl, have hm := number.min_prop α, cases number.min α with m; unfold min_prop at hm, begin specialize hm bnd, cases hm with m' hm', specialize h₁ m', unfold sem at h₁, simp [ht,hb,hl',hs',sem] at h₁, have := iff.mp lt_iff_le_not_le hm', simp [h₁,this] at h₁, contradiction, end, specialize hm bnd, specialize h₁ m, unfold sem at h₁, simp [ht,hb,hl',hs'] at h₁, unfold sem at h₁, have := le_antisymm hm h₁, simp [this], end, -- next case replace words "lower" with "upper" and "min" with "max"
Mario Carneiro (Apr 12 2018 at 06:06):
You are not likely to be able to simplify this unless you formalize the symmetry between min
and max
here
Mario Carneiro (Apr 12 2018 at 06:07):
You can try to extract a lemma with something with the same type as min
and max
Nima (Apr 12 2018 at 06:09):
If I don't extract any lemma, is there any way, I can write this proof but replace for example (is_lower_bound_is_lower c ht)
with a placeholder and later fill that placeholder twice?
Mario Carneiro (Apr 12 2018 at 06:11):
You can run the same tactic script twice. As long as you set up the state (before or after) with suitable modifications, you can do this kind of thing
Mario Carneiro (Apr 12 2018 at 06:12):
For example, you could have have lem := is_lower_bound_is_lower
in one branch and have lem := is_upper_bound_is_upper
in the other branch, and then run a script that refers to lem
which means different things in the two branches
Nima (Apr 12 2018 at 06:14):
gotcha, thanks
Kenny Lau (Apr 12 2018 at 06:14):
or just copy it once already
Nima (Apr 12 2018 at 06:32):
How do I combine the following two:
unfold f1 at h1 h2 -- only at h1 and h2 unfold f1 -- only at goal (?)
Mario Carneiro (Apr 12 2018 at 06:40):
unfold f1
can also be written unfold f1 at |-
, and you can do both with unfold f1 at h1 h2 |-
Mario Carneiro (Apr 12 2018 at 06:40):
there's also a unicode version of |-
Nima (Apr 12 2018 at 06:51):
thanks
Simon Hudon (Apr 12 2018 at 11:50):
I wonder if wlog
might be of help for your repetition problem. Like Mario said, you'd need to formalize the symmetry some more but maybe wlog
can do some of that
Nima (Apr 12 2018 at 21:29):
Sorry, but how do I finish this example?
class number := (aa1 := tt) example [nn:number] : number.aa1 = tt := sorry
Simon Hudon (Apr 12 2018 at 21:30):
Try by simp!
Simon Hudon (Apr 12 2018 at 21:30):
(the !
is part of the proof)
Nima (Apr 12 2018 at 21:31):
I got error: command expected
Simon Hudon (Apr 12 2018 at 21:32):
Restart your Lean server (C-c C-r in emacs)
Nima (Apr 12 2018 at 21:33):
I am on VS Code, restarted the whole program. But no luck!
class number := (aa1 := tt) example [nn:number] : number.aa1 = tt := by simp! -- error command expected
Lean 3.3.0
Simon Hudon (Apr 12 2018 at 21:34):
Oh! 3.3.0 ...
Simon Hudon (Apr 12 2018 at 21:34):
ok, one sec
Simon Hudon (Apr 12 2018 at 21:34):
ok by refl
should do it
Nima (Apr 12 2018 at 21:36):
invalid apply tactic, failed to unify number.aa1 = tt with ?m_2 = ?m_2 state: nn : number ⊢ number.aa1 = tt
Simon Hudon (Apr 12 2018 at 21:37):
What happens if you do:
begin unfold number.aa1 end
Simon Hudon (Apr 12 2018 at 21:39):
Sorry, I just got what is happening. The proof shouldn't work
Nima (Apr 12 2018 at 21:40):
Yep, it does not.
Simon Hudon (Apr 12 2018 at 21:41):
In class number := (aa1 := tt)
, aa1
is a field of type bool
whose default value (when you build an instance) is tt
. You can still specify a different value so you don't know for sure that aa1
is tt
in your example
Simon Hudon (Apr 12 2018 at 21:42):
What are you trying to do with that example? I suspect you're misusing classes
Nima (Apr 12 2018 at 21:45):
First, how do I change a default value? Is this like a programming language that I can assign value to variables??
I wanted to show number.has_prev a
is decidable for any value a
. But I guess you are saying value of has_prev
can be changed later (can mark it like a constant! I am lost)
(has_prev := λ a:α, dense = ff ∧ some a ≠ min)
Simon Hudon (Apr 12 2018 at 21:48):
No, this is not about mutating variables. Think of class number
as a type declaration:
@[class] structure number := (aa1 : bool)
the := tt
part allows you to write:
instance : number := { }
and it is taken to mean:
instance : number := { aa1 := tt }
Nima (Apr 12 2018 at 21:49):
ooo!
Simon Hudon (Apr 12 2018 at 21:50):
And in keeping with desugaring the class
/ instance
syntax, the latter is equivalent to:
@[instance] def my_number_instance : number := { aa1 := tt }
Nima (Apr 12 2018 at 21:51):
What was I thinking!!
Thanks a lot.
Simon Hudon (Apr 12 2018 at 21:52):
No worries
Nima (Apr 15 2018 at 20:48):
How do I finish this example without using finish
?
inductive param | p1 | p2 | p3 example : param.p1 = param.p2 → false := sorry
Patrick Massot (Apr 15 2018 at 20:51):
example : param.p1 = param.p2 → false := λ h, param.no_confusion h
Mario Carneiro (Apr 15 2018 at 20:53):
you can also use by injection h
Nima (Apr 15 2018 at 20:58):
Thank you both
Patrick Massot (Apr 15 2018 at 20:59):
I think you could create new topics, instead of always reusing the same one. It would make it easier for other readers to decide whether they could learn something from your questions.
Nima (Apr 15 2018 at 21:30):
That is definitely an option. However, most (if not all) of my questions are trivially answered by experienced users like Simon or Mario. I look at "Fresh off the boat" as a topic that contains questions a new user like me with a very little experience might ask. Nothing deep, but they come from everywhere (for example, because I read a book and that book talks about lots of different things). That is how I see them related.
Of course, another option is to create one topic for basically every one of them. The point is, 1) I don't know the answer is in no_confusion
or injection
, so most likely the topic I would create won't have a meaningful representation of its content. 2) I doubt that a user will learn much just by reading a single one of them. Either, they already know the answer, or if they don't, they are likely to benefit from similar questions that now they would not know how to find.
I agree, for some concepts having a separate topic makes a lot of sense. For example, Code Generation, Meta Programming, mathlib (may be each with subtopics as well). But I don't see enough benefit to bloat list of topic by wanting every topic to be as specific as possible. Maybe having both kinds would be more helpful. Some are general-nothing-deep-scattered-subjects and some focused-deep-into-a-concent topics.
Please let me know what you think. If I see you guys prefer separate topics anyway, I will do my best to post that way.
Simon Hudon (Apr 15 2018 at 21:37):
I think you can already pick a good topic by describing the problem that you're having. In this case you could say "how to I prove that different constructors form distinct values". I don't think the topic should contain the answer to your question because, if someone has the same problem as you, they won't know to look in the thread you created.
Simon Hudon (Apr 15 2018 at 21:38):
You can also pick a very bad topic name and change it later as suggestions come in
Nima (Apr 15 2018 at 21:39):
No problem,
Thanks for letting me know your thought.
Last updated: Dec 20 2023 at 11:08 UTC