Zulip Chat Archive

Stream: new members

Topic: Subtypes


Brandon Brown (May 02 2020 at 21:57):

How does one create subtypes? Say I want a function of two parameters one is an arbitrary natural number, the other is a natural number < 100.

Bryan Gin-ge Chen (May 02 2020 at 22:07):

In this case I would just use fin:

def foo (m : ) (n : fin 100) := sorry

In general, {a // P a} where α : Type and P : α → Prop is notation for the subtype of terms of type α that satisfy P. Subtypes are mentioned briefly in TPiL in chapter 7. See also this mathlib doc page.

Brandon Brown (May 02 2020 at 22:13):

Thanks!

Brandon Brown (May 02 2020 at 22:13):

Why doesn't this work?

Brandon Brown (May 02 2020 at 22:13):

example :  := 4 -- ok
example : {n :  // n  100} := 4 --error

Bryan Gin-ge Chen (May 02 2020 at 22:20):

Because 4 is not a term of that subtype. You can construct a term like this:

import tactic
example : {n :  // n  100} := 4, by norm_num
-- dec_trivial also works and doesn't require any import

Brandon Brown (May 02 2020 at 22:24):

But example : fin 100 := 4 works

Bryan Gin-ge Chen (May 02 2020 at 22:33):

That's because 4 is a term of type fin 100:

set_option pp.numerals false
#check (4 : fin 100)
/-
bit0 (bit0 has_one.one) : fin (bit0 (bit0 (bit1 (bit0 (bit0 (bit1 has_one.one))))))
-/

See here. Note how Lean interprets numerals in terms of bit0, bit1 and has_one.one and has_zero.zero.

Kevin Buzzard (May 02 2020 at 23:30):

Just because the thing you defined, {n : ℕ // n ≤ 100} is "pretty much exactly the same thing as" fin 100, doesn't mean that everything that works for fin 100 will work for {n : ℕ // n ≤ 100}. There are lots of extra definitions which have been put on top of fin n, e.g. it has a 0, a 1, an add, and so on. If you want the same for your type you'll have to give it a name and put all these structure on it yourself.

Kevin Buzzard (May 03 2020 at 00:15):

Deleted

Brandon Brown (May 03 2020 at 01:14):

I want to implement a function that adds two lists of the same length element-wise, but I don't know how to make sure the lists are the same size. Should I instead return an option list so that if they're the same size (using if..then..else) then I can do some computation and return some list otherwise none ?

Jalex Stark (May 03 2020 at 02:44):

If you want lists of fixed length, you could use vectors :P
If you're doing it with lists, you probably want something like

example {α : Type} (a b : list α) (length_eq : list.length a = list.length b) : list α := sorry

Shing Tak Lam (May 03 2020 at 02:51):

If you want to define it for vectors, it'd look something like this:

import data.vector

def add_vecs {α : Type} [has_add α] {n : } : vector α n  vector α n  vector α n := sorry

Brandon Brown (May 03 2020 at 02:53):

Yeah I should do this with vectors I don't know why I'm using lists

Shing Tak Lam (May 03 2020 at 02:56):

Although if you want to define your function by pattern matching/recursion, it may be a bit more complicated than by just using lists.

Shing Tak Lam (May 03 2020 at 03:01):

and when you try to call the function, vectors is not as nice to use compared to lists.

Brandon Brown (May 03 2020 at 03:02):

Actually this works pretty well even for non-equal lists.

def list_add {α : Type u} [has_add α] : list α  list α  list α
| list.nil l2 := l2
| l1 list.nil := l1
| (h1 :: t1) (h2 :: t2) := (h1 + h2) :: (list_add t1 t2)

#reduce list_add [5,1] [5,2] -- [10, 3]
#reduce list_add [1,3] [2,3,3] -- [3, 6, 3]

Brandon Brown (May 03 2020 at 03:09):

I can create a type class instance for it

instance list_has_add {α : Type u} [has_add α] :
has_add (list α) := list_add

#reduce [1,2] + [3,4] -- [4,6]

I don't know what the brackets for ⟨list_add⟩ are doing. I'm familiar with the angle brackets being used to create sigma types and and-conjunction but not sure what it means in this context

Shing Tak Lam (May 03 2020 at 03:11):

It's the short version for

instance list_has_add {α : Type u} [has_add α] :
has_add (list α) := {add := list_add}

Brandon Brown (May 03 2020 at 03:17):

Oh so {add := list_add} is a structure

Shing Tak Lam (May 03 2020 at 03:20):

has_add is a class, but I think so?

Better wait for an expert to chime in (I think most/all are asleep right now). In the mean time, I think this is the correct section of TPIL.

https://leanprover.github.io/theorem_proving_in_lean/type_classes.html#type-classes-and-instances

Let us start with the first step of the program above, declaring an appropriate class:

class inhabited (α : Type _) :=
(default : α)

The command class above is shorthand for

@[class] structure inhabited (α : Type _) :=
(default : α)

Brandon Brown (May 03 2020 at 03:22):

Ah I didn't catch that. So a type class _is_ a structure

Shing Tak Lam (May 03 2020 at 03:24):

(I think there is a difference? But I'm not sure about this myself, so this is probably when you wait for an expert to come by. I think the Europeans will wake up in a few hours)

Brandon Brown (May 03 2020 at 03:26):

Fair enough, I'm in Chicago so I'm off to bed soon - hopefully will wake to some more clarification. But thanks for your help!

Mario Carneiro (May 03 2020 at 03:33):

there is no difference

Mario Carneiro (May 03 2020 at 03:36):

class is sugar for @[class] structure

Brandon Brown (May 03 2020 at 03:41):

Thanks for clarifying. Then what is an instance ? Is that also syntactic sugar for something I'm familiar with already?

Mario Carneiro (May 03 2020 at 03:50):

instance is sugar for @[instance] def

Mario Carneiro (May 03 2020 at 03:51):

except that with instance the name is optional (else it makes a name by a simple heuristic), while def requires a name

Mario Carneiro (May 03 2020 at 03:52):

However, the @[class] and @[instance] attributes add additional behavior in conjunction with the [foo x] instance binder to trigger type class inference

Mario Carneiro (May 03 2020 at 03:52):

but from a foundational standpoint, an instance is just a def and a class is just a structure (and a structure is just an inductive)

Brandon Brown (May 03 2020 at 03:57):

Thanks! That makes things much clearer.

Brandon Brown (May 03 2020 at 04:43):

Just found this definition of vector in the standard library
def vector (α : Type u) (n : ℕ) := { l : list α // l.length = n }
That seems quite elegant compared to the one in TPIL

Mario Carneiro (May 03 2020 at 05:00):

a lot of people think the dependent family version is better, because it makes it easier to define functions by induction on vectors

Mario Carneiro (May 03 2020 at 05:01):

however, the example in TPIL is mostly there as an example of a dependent type family

Brandon Brown (May 03 2020 at 05:05):

It's weird that in order to create a vector using the subtype version, you have to provide a list along with a proof that it is the right length; which makes me like the TPIL one better now.
def myvec : vector ℕ 2 := ⟨[1,2], dec_trivial⟩

Mario Carneiro (May 03 2020 at 05:08):

You can still create a vector using a cons function with the same type as the TPIL one so that you don't need a side proof

Mario Carneiro (May 03 2020 at 05:08):

it's just a definition now instead of a constructor

Brandon Brown (May 03 2020 at 05:15):

How do you do that?

Mario Carneiro (May 03 2020 at 05:15):

Look up the definition of vector.cons in the core library

Brandon Brown (May 03 2020 at 05:19):

Ah I see, nice. def myvec2 (n : vector ℕ 0): vector ℕ 2 := vector.cons 1 (vector.cons 2 n)


Last updated: Dec 20 2023 at 11:08 UTC