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
.