Zulip Chat Archive
Stream: general
Topic: breaking inductive
Leonard Wiechmann (Feb 21 2022 at 14:30):
hey all, i've been messing with inductive
to test my understanding of the typing rules wrt universe levels.
i've found 2 cases that i'm unsure about:
-- 1: incomplete recursive arguments.
-- this doesn't and shouldn't work. `r: nat -> Sort 1`, which has type Sort 2, which is too large.
inductive T1: nat -> Sort 1
| c (r: T1): T1 0
-- but this should work, right? `r: nat -> Prop`, which has type Prop = Sort 0 and 0 <= 0.
inductive T0: nat -> Sort 0
| c (r: T0): T0 0
-- ^^
-- it gives me an error here:
--type expected at
-- T0
--term has type
-- ℕ → Prop
-- that error doesn't really make sense: `\N -> Prop` is a type?
-- if anything, i'd have expected `r: T0` to error.
-- 2: definition of Vec doesn't work for generic universe level.
-- this errors: arg 3 to cons (ts) is too large, which it isn't.
-- this same definition works when using Type instead of Sort.
inductive {u} Vec (T: Sort u): nat -> Sort u
| nil: Vec 0
| cons (t: T) (n: nat) (ts: Vec n): Vec n.succ
-- but the definition for any concrete universe level is valid...
-- works.
inductive Vec0 (T: Sort 0): nat -> Sort 0
| nil: Vec0 0
| cons (t: T) (n: nat) (ts: Vec0 n): Vec0 n.succ
-- works.
inductive Vec1 (T: Sort 1): nat -> Sort 1
| nil: Vec1 0
| cons (t: T) (n: nat) (ts: Vec1 n): Vec1 n.succ
-- works.
inductive Vec42 (T: Sort 42): nat -> Sort 42
| nil: Vec42 0
| cons (t: T) (n: nat) (ts: Vec42 n): Vec42 n.succ
Leonard Wiechmann (Feb 21 2022 at 14:32):
i think both examples should work.
which is bugged: my understanding or lean?
Reid Barton (Feb 21 2022 at 14:32):
In 1, both r : T1
and r : T0
don't make any sense. I guess Lean's error reporting got confused.
Reid Barton (Feb 21 2022 at 14:34):
In 2, it is not a bug, but maybe a limitation? You can't create an inductive which is a Prop for some values of the universe parameters, and a Type for others--I think it has something to do with what recursor to generate.
Leonard Wiechmann (Feb 21 2022 at 14:34):
yeah, example 1 doesn't make sense semantically.
but shouldn't r: T0
for T0: nat -> Prop
work? -- just according to the universe levels. so make sense "syntactically"?
Reid Barton (Feb 21 2022 at 14:35):
no
Leonard Wiechmann (Feb 21 2022 at 14:36):
Reid Barton said:
In 2, it is not a bug, but maybe a limitation? You can't create an inductive which is a Prop for some values of the universe parameters, and a Type for others--I think it has something to do with what recursor to generate.
ok, that makes sense. it can't generate both recursors.
Reid Barton (Feb 21 2022 at 14:36):
in r : T0
, T0
had better be a type, but whoops it is a function T0 : nat -> Prop
instead
Reid Barton (Feb 21 2022 at 14:36):
Has nothing to do with universes.
Mario Carneiro (Feb 21 2022 at 14:37):
Reid Barton said:
In 2, it is not a bug, but maybe a limitation? You can't create an inductive which is a Prop for some values of the universe parameters, and a Type for others--I think it has something to do with what recursor to generate.
Actually you can, it's just not advisable
Leonard Wiechmann (Feb 21 2022 at 14:37):
oh, I think I see where I got confused.
i thought T0 = nat -> Prop
and just substituted.
Mario Carneiro (Feb 21 2022 at 14:38):
Lean 4 disallows this in the elaborator now but you can still create them if you ask the kernel directly
Leonard Wiechmann (Feb 21 2022 at 14:38):
Mario Carneiro said:
Lean 4 disallows this in the elaborator now but you can still create them if you ask the kernel directly
does it generate the recursor for Type
, or both?
Mario Carneiro (Feb 21 2022 at 14:39):
It generates the recursor for Prop
Leonard Wiechmann (Feb 21 2022 at 14:39):
well, that makes sense :D
Mario Carneiro (Feb 21 2022 at 14:39):
that is, it is a small eliminating type
Mario Carneiro (Feb 21 2022 at 14:39):
it's a recursor for all variations of the type, but the motive targets Prop
Leonard Wiechmann (Feb 21 2022 at 14:40):
right, because it isn't LE for all universe levels
Mario Carneiro (Feb 21 2022 at 14:40):
inductive {u} foo : Sort u
| a : foo
| b : foo
set_option pp.universes true
#print foo.rec
-- protected eliminator {u} foo.rec : ∀ {motive : foo.{u} → Prop}, motive foo.a.{u} → motive foo.b.{u} → ∀ (n : foo.{u}), motive n
Leonard Wiechmann (Feb 21 2022 at 14:41):
you need 2 constructors to not be subsingleton, right?
Mario Carneiro (Feb 21 2022 at 14:41):
right
Mario Carneiro (Feb 21 2022 at 14:41):
or other things
Leonard Wiechmann (Feb 21 2022 at 14:42):
ok, I think i've cleared up my confusion, thanks guys!
Mario Carneiro (Feb 21 2022 at 14:42):
the fun part I like to show with this example is that foo.a.{1}
and foo.b.{1}
are neither provably equal nor provably distinct
Reid Barton (Feb 21 2022 at 14:44):
ah right, I was misremembering how the example I wanted to work fails
inductive {u} M (X : Sort u) : Sort u
| mk : X → M
Reid Barton (Feb 21 2022 at 14:44):
This seems like it should be large-eliminating but Lean still makes an eliminator into Prop only
Mario Carneiro (Feb 21 2022 at 14:46):
The real reason your Vec
example doesn't work is because lean doesn't know that imax 1 u <= u
Mario Carneiro (Feb 21 2022 at 14:47):
here's a way you can check level inequalities:
inductive {u} foo : Sort u
| a : pempty.{imax 1 u} → foo
this succeeds iff you put an expression v
for the pempty
such that v <= u
is true
Mario Carneiro (Feb 21 2022 at 14:49):
The system given in my paper will validate imax 1 u <= u
, because it does case disjunction when it can't make progress on the inequality. Lean doesn't, it uses a known-incomplete set of rewrite rules to normalize universe expressions, so you can get some spuriously rejected examples like this
Leonard Wiechmann (Feb 21 2022 at 15:17):
here is something else i'm unsure about:
why are only the universe levels of the constructor "arguments" validated and not also the "parameters"?
inductive foo (X: Sort 42): Sort 1
| ctor: foo
-- foo.ctor : Π {X : Type 41}, foo X
the full foo.ctor
also takes X
, but universe(X) > 1
.
i probably don't understand this, because i don't really understand where the <=
constraint for arguments comes from in the first place.
is there a somewhat simple answer that's not "because things work out that way, and everything else would be limiting or inconsistent"? maybe some paper that discusses the rationale?
Mario Carneiro (Feb 21 2022 at 17:27):
Well, it would be inconsistent to have a small type containing an injective copy of a large type
Mario Carneiro (Feb 21 2022 at 17:30):
like
inductive foo : Type
| ctor : set Type -> foo
Intuitively, this is cantor's paradox, although it doesn't immediately follow. The correct paradox to cite is Girard's paradox
Mario Carneiro (Feb 21 2022 at 17:31):
For parameters this isn't really what's happening. It's more like we have a construction of an inductive type which depends on some fixed parameters, i.e. a family of inductive constructions. These play the role of constants so you can't use them to pull any funny business
Mario Carneiro (Feb 21 2022 at 17:32):
In particular, foo
is not injective with respect to its parameters. It took a discovered inconsistency in Agda to realize that this had to be the case, but it's pretty obvious if you look at your foo
that it has to be noninjective
Mario Carneiro (Feb 21 2022 at 17:33):
You can't prove it in lean but it is consistent for foo X = unit
for all values of X
Leonard Wiechmann (Feb 22 2022 at 13:20):
Mario Carneiro said:
Well, it would be inconsistent to have a small type containing an injective copy of a large type
ok, this seems to make sense:
inductive Trojan : Type
| intro (soldier: Type) : Trojan
def extract: Trojan -> Type :=
λ t, Trojan.rec (λ soldier, soldier) t
-- this is *effectively* `Type -> nat`, but it's type is Type, which is paradoxical.
def troy: Trojan -> nat :=
λ t, (λ soldier, ...) (extract t)
Leonard Wiechmann (Feb 22 2022 at 13:21):
Mario Carneiro said:
For parameters this isn't really what's happening. It's more like we have a construction of an inductive type which depends on some fixed parameters, i.e. a family of inductive constructions. These play the role of constants so you can't use them to pull any funny business
this seems to intuitively make sense, because the "callsite" also needs to know about the parameters (and thus their Sorts). so you can't do funny extraction business.
Leonard Wiechmann (Feb 22 2022 at 13:24):
the last 2, I don't really understand:
1: what does foo
is not injective wrt its parameters mean? is there a link to said agda inconsistency?
2: what does foo X = unit
mean? guessing this refers to my foo from above. inductive foo (X: Sort 42) : unit
doesn't make sense, because unit is not a sort.
Mario Carneiro (Feb 22 2022 at 13:39):
Leonard Wiechmann said:
what does
foo
is not injective wrt its parameters mean? is there a link to said agda inconsistency?
Agda inconsistency: https://coq-club.inria.narkive.com/iDuSeltD/agda-with-the-excluded-middle-is-inconsistent
Injectivity here is saying that foo X = foo Y -> X = Y
, i.e. foo
is injective as a function on types
Mario Carneiro (Feb 22 2022 at 13:40):
This is not possible because there are too many possible values of X : Sort 42
compared to the number of values of foo X : Type
, i.e. it's essentially a cardinality constraint
Mario Carneiro (Feb 22 2022 at 13:42):
When I say foo X = unit
, I mean that you could add the axiom axiom foo_is_unit (X) : foo X = unit
and the resulting system would still be consistent. (You can already prove that foo X ≃ unit
using the way it has been defined, but type equalities are almost never provable in lean. This non-injectivity example is a rare case where you can actually prove an equality of types
Leonard Wiechmann (Feb 24 2022 at 11:45):
Mario Carneiro said:
When I say
foo X = unit
, I mean that you could add the axiomaxiom foo_is_unit (X) : foo X = unit
and the resulting system would still be consistent. (You can already prove thatfoo X ≃ unit
using the way it has been defined, but type equalities are almost never provable in lean. This non-injectivity example is a rare case where you can actually prove an equality of types
oh, I see. because foo is basically unit, except with a parameter.
Leonard Wiechmann (Feb 24 2022 at 11:45):
Mario Carneiro said:
Leonard Wiechmann said:
what does
foo
is not injective wrt its parameters mean? is there a link to said agda inconsistency?Agda inconsistency: https://coq-club.inria.narkive.com/iDuSeltD/agda-with-the-excluded-middle-is-inconsistent
Injectivity here is saying thatfoo X = foo Y -> X = Y
, i.e.foo
is injective as a function on types
ok, awesome, thanks! i'll check it out once i've got some time.
Last updated: Dec 20 2023 at 11:08 UTC