# 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 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

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 that`foo 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