Si prefieren usar este tiempo para trabajar en tácticas avanzadas, aquí les va un archivo para practicar:
square : Nat → Nat o fact : Nat → Nat. Estas son funciones simplemente tipadas, de un tipo a otro tipo.Prod : Type → Type → Type o Sum, que toman tipos como entradas y devuelven un tipo. Otro ejemplo común sería List : Type → Type.X, el tipo List X se define inductivamente: se comienza con la lista vacía y luego se anteponen términos de tipo X.inductive List (X : Type) : Type
| nil : List X
| cons : X → List X → List X
Hasta el momento, hemos visto:
Debería haber:
Una función polimorfa es una función que toma un tipo como argumento. La función polimorfa más simple es la función identidad:
def id {X : Type} : X → X := fun x ↦ x
#check id -- id {X : Type} : X → X
id es, en efecto, un término que depende (implícitamente) de un tipo. Por ejemplo, id (X := Nat) (también denotado por @id Nat) es un término de tipo Nat → Nat, es decir, una función de Nat a Nat.id es un ejemplo de un término sobrecargado: para cualquier tipo X, podemos denotar su función identidad simplemente como id, por lo que una expresión como id (3 : Nat) está bien tipada (y es de tipo Nat).X es una función F: X → Type, es decir, tipos que dependen de términos: para todo x : X, F x es un tipo.Tuplas : Nat → Type tal que Tuplas n es el tipo de las listas de enteros de longitud n:Tuplas n := {L : List Int // List.length L = n}.List Int, que representa la suma de todos los Tuplas n.F: X → Type, podemos construir el (x : X) × F x, cuyos términos se llaman pares dependientes y son de la forma ⟨x, t⟩ donde x : X y t : F x.⟨2, [1, -1]⟩ es un término del tipo (n : Nat) × Tuplas n. Otra notación posible sería Sigma Tup, donde Tuplas : Nat → Type. Y la notación clásica en teoría de tipos sería:x : X, tenemos F x = Y, entonces ((x : X) × F x) = (X × Y).Dada una familia de tipos F: X → Type, el Sigma F así definido se puede denotar (x : X) × F x.
inductive Sigma {X : Type} (F : X → Type)
| mk (x : X) (t : F x) : Sigma F
Se puede definir una proyección al primer factor por pattern matching (lo cual hace uso del principio de inducción asociado al tipo inductivo Sigma F):
def pr₁ {X : Type} {F : X → Type} : Sigma F → X
| Sigma.mk x t => x
F : X → Type para definir los llamados f : (x : X) → F x cuyo tipo de retorno depende del valor de entrada.(x : X) → F x sería:x : X, tenemos F x = Y, entonces ((x : X) → F x) = (X → Y).cero-tupla : (n : Nat) → Tuplas n. Procederemos por inducción.n = 0, se pone cero-tupla 0 := ([] : List Int) (la lista vacía). Si cero-tupla n : Tuplas n ya está definido, se pondrá cero-tupla (n + 1) := (0 :: cero-tupla n), que de hecho es de tipo Tuplas (n + 1).def pr₂ {X : Type} {F : X → Type} : (p : Sigma F) → F (pr₁ p)
| Sigma.mk x t => t
#check al tipo List que hemos definido, encontrarán que es de tipo Type → Type. Para poder escribir esto, necesitamos tratar Type como un término. ¿Pero cuál es su tipo?Type es un término de tipo Type 1. Como consecuencia, formadores de tipos con valores en Type también son términos de tipo Type 1.Prop es un universo, de tipo Type (también denotado por Type 0). Pueden pensar en Type l como el universo de tipos de nivel l.z : ℂ y consideremos la proposición z ^ 2 = -1. Ya que proposiciones son tipos, esto define una familia de tipos F : ℂ → Prop.⟨z, p⟩, donde p : z ^ 2 = -1 (así que p es una prueba de que z ^ 2 = -1), como una prueba de que -1 tiene una raíz cuadrada en ℂ.P : X → Prop sobre un tipo X, la proposición ∃ x : X, P x es la proposición definida inductivamente de la siguiente manera.inductive Exists {X : Type} (P : X → Prop) : Prop
| intro (x : X) (p : P x) : Exists P
∃ x : X, P x, se necesita construir un término x : X (un testigo) y una demostración de la proposición P x (la prueba).¬(∀ x : X, ¬(P x)) es más débil que decir que ∃ x : X, P x.x : ℝ y consideremos la proposición x ^ 2 ≥ 0. Pensando en proposiciones-como-tipos, esto define una familia de tipos F : ℝ → Prop.f : (x : ℝ) → x ^ 2 ≥ 0 envía un número real x a una prueba de que x ^ 2 ≥ 0. Así que podemos interpretar tal función dependiente f como una prueba de la proposición ∀ x : ℝ, x ^ 2 ≥ 0.P : X → Prop sobre un tipo X, la proposición ∀ x : X, P x es la proposición definida por el tipo de las funciones dependientes (x : X) → P x.fun x ↦ _ (si estamos en modo término) o intro x (si estamos en modo táctico).∀ w : ℂ, ∃ z : ℂ, z ^ 2 = w, entonces intro w cambia el objetivo a ∃ z : ℂ, z ^ 2 = w, para un w que ahora está fijado. Luego tenemos que construir una raíz cuadrada de ese w para concluir.z : ℂ para la propiedad z ^ 2 = w, ¡pero esta información no se puede recuperar del enunciado existencial!A para el cual se cumple la siguiente propiedad: (a b : A) → a = b, es decir que a dos términos cualesquiera a y b de tipo A, podemos asociar una identificación a = b.A := (x = y) para x, y en un tipo X cualquiera son todos proposiciones. El estudio detallado de los tipos de igualdad es un tema fundamental en teoría homotópica de tipos. Ahí se demuestra, por ejemplo, que para todo tipo A, el tipo IsProp A es una proposición theorem FLT {n : Nat} {x y z : Int} : (n > 2) → x ^ n + y ^ n = z ^ n → x * y * z = 0
FLT definido arriba es una proposición F : X → Type, definimos un (x : X) × (F x)) y un (x : X) → F x).P : X → Prop, la proposición ∃ x : X, P x es la proposición cuyas pruebas se construyen usando pares dependientes (x : X) × (P x). Y una prueba de la proposición ∀ x : X, P x es una función dependiente (x : X) → P x.