Académique Documents
Professionnel Documents
Culture Documents
El Isomorfismo de Curry-Howard
Favio Ezequiel Miranda Perea
Araceli Liliana Reyes Cabello
Lourdes Del Carmen Gonzalez Huesca
23 de mayo de 2016
Facultad de Ciencias UNAM
1.
A continuaci
on presentamos un lenguaje para expresiones aritmeticas y booleanas que incluye
las operaciones de suma, producto, orden, un test para cero y la negacion booleana.
Las expresiones del lenguaje se definen como sigue:
e ::= x | n | true | false |
e+e|ee
e < e | iszero e | not e
Ejemplos de expresiones del lenguaje son:
3+y
not(iszero 9)
iszero 3 < not(2 + 4)
not false
not(x + 1)
iszero false
not 7 y
7<x+y
Se observa que todas son expresiones sintacticamente correctas pero algunas de ellas no tienen
sentido desde el punto de vista sem
antico, es decir, no es posible asignarles uno de los tipos posibles
para el lenguaje que son Nat o Bool. Por ejemplo not(8 + 2) o iszero false. Ademas hay expresiones
en donde no resulta claro si son v
alidas o no, por ejemplo x + 2 puede o no ser valida dependiendo
de si x representa a un n
umero o a un booleano, lo mismo sucede con not(iszero x) o con x < y + 1.
1
1.1.
Int
erprete
Un interprete para el lenguaje es simplemente una funcion que devuelve un natural o un booleano
que resulta de evaluar una expresi
on e. La definicion recursiva de esta funcion
eval : Exp N B
donde N = {0, 1, . . .} y B = {t, f } es:
eval
eval
eval
eval
eval
eval
eval
eval
n = n
true = t
false = f
(e1 + e2) = eval e1 +
(e1 * e2) = eval e1 *
(e1 < e2) = (eval e1)
(not e) = (eval e)
(iszero e) = isz (eval
eval e2
eval e2
< (eval e2)
e)
donde los smbolos del lado derecho +, , < denotan las operaciones usuales mientras que del
lado izquierdo son u
nicamente smbolos que forman parte de las expresiones. Omitmos aqu el
uso de variables por simplicidad en la presentacion. Para agregar variables es necesario agregar un
estado como argumento de entrada al interprete, el cual se encargara de evaluar variables.
Se observa que la funci
on de evaluacion as definida resulta una funcion parcial puesto que la
evaluacion de expresiones como (35)+not false no tiene sentido ya que no sabemos sumar 15+true.
Una manera de evitar estos errores es verificando previamente si la expresion es coherente para
lo cual podemos usar la deducci
on natural.
1.2.
Eliminaci
on de expresiones sem
anticamente incorrectas
6` Bool(iszero(3 7) + 4)
Esta clase de verificaciones son exactamente lo que hace un sistema de tipos en lenguajes de
programacion.
2.
Sistemas de tipos
(T N um)
(V ar)
` e1 : Nat ` e2 : Nat
(T Sum)
` e1 + e2 : Nat
Type-checking error
` e1 : Nat ` e2 : Nat
(T P rod)
` e1 e2 : Nat
` true : Bool
(T true)
` false : Bool
(T f alse)
` e1 : Nat ` e2 : Nat
(T men)
` e1 < e2 : Bool
` e : Nat
` e : Bool
(T isz)
(T not)
` iszero e : Bool
` not e : Bool
Mediante estas reglas podemos cerciorarnos por ejemplo de que si x : Nat entonces iszero(x + 2) :
Bool como sigue:
1. x : Nat ` x : Nat (V ar)
2. x : Nat ` 2 : Nat (T N um)
3. x : Nat ` x + 2 : Nat (T Sum) 1, 2
4. x : Nat ` iszero(x + 2) : Nat (T isz) 4
Veamos otro ejemplo, esta vez tipando la expresion not(2 + x < y) en el contexto = {x :
Nat, y : Nat}
1. x : Nat, y : Nat ` x : Nat (V ar)
2. x : Nat, y : Nat ` 2 : Nat (T N um)
3. x : Nat, y : Nat ` 2 + x : Nat (T Sum) 1, 2
4. x : Nat, y : Nat ` y : Nat (V ar)
5. x : Nat, y : Nat ` 2 + x < y : Bool (T men) 3, 4
6. x : Nat, y : Nat ` not(2 + x < y) : Bool (T neg) 5
3.
Isomorfismo de Curry-Howard
Como se observa los sistemas de tipos son muy similares a los sistemas de deduccion natural
con contextos que ya hemos estudiado. De hecho los sistemas de deduccion natural pueden verse,
desde el punto de vista computacional, como un sistema de tipos para un prototipo de lenguaje de
programacion funcional mediante la llamada correspondencia de Curry-Howard, tambien conocida
como paradigma de derivaciones como programas o proposiciones como tipos. La idea a grandes
rasgos es:
Las formulas de la l
ogica corresponden a tipos para un lenguaje de programacion.
Las pruebas o derivaciones en la logica corresponden a los pasos que el verificador de tipos
realiza para mostrar que cierta expresion o programa del lenguaje de programacion est
a bien
tipada.
Por lo tanto, verificar si una prueba es correcta en un contexto dado corresponde a verificar
si un programa tiene un tipo v
alido en el contexto dado.
4
4.
La relaci
on de inferencia ` A se modifica anotando a cada prueba de un secuente ` A
mediante una expresi
on t que que corresponde a un programa de un prototipo de lenguaje de programacion, esta expresi
on t resulta ser tambien un codigo de la prueba cuyo secuente final es ` A.
De esta manera obtenemos una relacion ` t : A donde = {x1 : A1 , . . . , xn : An } es un
contexto de declaraci
on de variables xi : Ai consideradas locales. En este caso no solo suponemos
una formula Ai sino que tambien suponemos que existe una prueba xi de Ai . La nueva relaci
on
entre contextos , expresiones o c
odigos t y formulas A, puede entenderse desde dos puntos de vista
distintos:
Logicamente: ` t : A significa que la formula A es derivable a partir de las hipotesis y t
es un c
odigo de prueba para la derivacion ` A.
Computacionalmente: ` t : A significa que t es un programa con tipo A cuyas variables
locales est
an declaradas en .
La nueva relaci
on de inferencia se define recursivamente a partir de la regla de inicio:
, x : A ` x : A (Hip)
mediante reglas de introducci
on y eliminacion para cada conectivo
Implicaci
on: el conectivo corresponde al tipo de funciones.
, x : A ` t : B
`f :AB `t:A
( I)
( E)
` fun(x : A.t) : A B
`ft:B
Conjunci
on: el conectivo corresponde al tipo , es decir al producto cartesiano de dos tipos.
`t:A `s:B
(I)
` ht, si : A B
`t:AB
(2 E)
` fst t : A
`t:AB
(1 E)
` snd t : B
Disyunci
on: el conectivo corresponde al tipo +, es decir a la union disjunta de dos tipos.
`t:A
(1 I)
` inl t : A B
`t:B
(2 I)
` inr t : A B
` r : A B , x : A ` s : C , y : B ` t : C
(E)
` case r of inl x s | inr y t : C
Falso: la constante de falsedad corresponde a un tipo vaco.
`r:
(E)
` abort r : A
5.
Ejemplos
En los siguientes ejemplos se trata de hallar el programa t que codifique la prueba correspondiente. Como ejercicios el lector debe justificar cada paso y/o agregar algunos pasos faltantes.
`t:AA
1. x : A ` x : A
2. ` fun(x : A.x) : A A
`t:AAB
1. x : A ` x : A
2. x : A ` inl x : A B
3. ` fun(x : A. inl x) : A A B
`t:AB B
1. x : A B ` x : A B
2. x : A B ` snd x : B
3. ` fun(x : A B. snd x) : A B B
f : A B, g : B C ` t : A C
1. f : A B, g : B C, x : A ` f : A B
2. f : A B, g : B C, x : A ` g : B C
3. f : A B, g : B C, x : A ` x : A
4. f : A B, g : B C, x : A ` f x : B
5. f : A B, g : B C, x : A ` g(f x) : C
6. f : A B, g : B C ` fun(x : A.g(f x)) : A C
`: t : B (A B)
1. x : B, y : A ` x : B
2. x : B ` fun(y : A.x) : A B
3. ` fun(x : B. fun(y : A.x)) : B (A B)
` t : (A B C) (A B C)
1. f : A B C, x : A, y : B ` f : A B C
2. f : A B C, x : A, y : B ` x : A
3. f : A B C, x : A, y : B ` y : B
4. f : A B C, x : A, y : B ` hx, yi : A B
5. f : A B C, x : A, y : B ` f hx, yi : C
6. f : A B C, x : A ` fun(y : B.f hx, yi) : B C
6
7. f : A B C ` fun x : A. fun(y : B.f hx, yi) : A B C
8. ` fun f : A B C. fun x : A. fun(y : B.f hx, yi) : (A B C) A B C
f :AB C `t:AB C
1. f : A B C, x : A B ` f : A B C
2. f : A B C, x : A B ` f : A B C
3. f : A B C, x : A B ` x : A B
4. f : A B C, x : A B ` fst x : A
5. f : A B C, x : A B ` snd x : B
6. f : A B C, x : A B ` f (fst x) : B C
7. f : A B C, x : A B ` f (fst x)(snd x) : C
8. f : A B C ` fun x : A B.f (fst x)(snd x) : A B C
f :AB C `t:AC
1. f : A B C, x : A ` f : A B C
2. f : A B C, x : A ` x : A
3. f : A B C, x : A ` inl x : A B
4. f : A B C, x : A ` f (inl x) : C
5. f : A B C ` fun(x : A.f (inl x)) : A C
x : A B, f : A C, g : B C ` t : C
1. x : A B, f : A C, g : B C ` x : A B
2. x : A B, f : A C, g : B C, y : A ` f y : C
3. x : A B, f : A C, g : B C, z : B ` gz : C
4. x : A B, f : A C, g : B C ` ( case x of inl y f y | inr z gz) : C
f : A B, g : C D, x : A C ` t : B D
1. f : A B, g : C D, x : A C ` x : A C
2. f : A B, g : C D, x : A C, y : A ` inl(f y) : B D
3. f : A B, g : C D, x : A C, z : C ` inr(gz) : B D
4. f : A B, g : C D, x : A C ` ( case x of inl y inl(f y) | inr z inr(gz)) : B D
x : (A C) (B C), f : (A B) ` t : C
1. x : (A C) (B C), f : (A B) ` fst x : A C
2. x : (A C) (B C), f : (A B) ` snd x : B C
3. x : (A C) (B C), f : (A B), y : A ` f y : B
4. x : (A C) (B C), f : (A B), y : A ` (snd x)(f y) : C
7
5. x : (A C) (B C), f : (A B), z : C ` z : C
6. x : (A C) (B C), f : (A B) ` ( case x of inl y inl(snd x)(f y) | inr z z) : C
x : A (B C) ` t : (A B) (A C)
1. x : A (B C) ` fst x : A
2. x : A (B C) ` snd x : B C
3. x : A (B C), y : B ` hfst x, yi : A B
4. x : A (B C), y : B ` inlhfst x, yi : (A B) (A C)
5. x : A (B C), z : C ` inrhfst x, zi : (A B) (A C)
6. x : A(BC) ` ( case snd x of inl y inlhfst x, yi | inr z inrhfst x, zi) : (AB)(AC)
x : (A B) (A C) ` t : A (B C)
1. x : (A B) (A C), y : A B ` inl(snd y) : B C
2. x : (A B) (A C), y : A B ` hfst y, inl(snd y)i : A (B C)
3. x : (A B) (A C), z : A C ` hfst z, inr(snd z)i : A (B C)
4. x : (A B) (A C) ` ( case x of inl y hfst y, inl(snd y)i | inr z hfst z, inr(snd z)i :
A (B C)
x : A B, f : B C D, g : A C ` t : D
1. x : A B, f : B C D, g : A C, y : A ` inr(gy) : B C
2. x : A B, f : B C D, g : A C, y : A ` f inr(gy) : D
3. x : A B, f : B C D, g : A C, z : B ` f (inl z) : D
4. x : A B, f : B C D, g : A C ` ( case x of inl y f inr(gy) | inr z f (inl z) : D
6.
Pruebas redundantes
Dadas las derivaciones ` fun(x : A.e) : A B y ` r : A construir una derivaci
on de
`? : B.
La soluci
on que salta a la vista es ` fun(x : A.e) r : B
Sin embargo de las reglas y la prueba dada de A B, sabemos que previamente
debio obtenerse la prueba , x : A ` e : B puesto que la regla usada fue ( I). Ahora
bien, observese que como ya tenemos una prueba ` r : A, no es necesario suponer
x : A puesto que en la prueba codificada por e podemos sustituir cada prueba x de
A por la prueba dada r, obteniendo as una prueba de B sin necesidad de usar el
modus ponens, a saber ` e[x := r] : B.
Dada la derivaci
on ` he1 , e2 i : A B construir una prueba ` ? : A.
La respuesta m
as inmediata es ` fsthe1 , e2 i : A.
Pero observese que para construir la prueba codificada por fsthe1 , e2 i antes tuvo que
construirse una prueba codificada por e1 que tiene que ser ` e1 : A puesto que la
regla utilizada fue (I).
8
De lo anterior se observa que las primeras pruebas son redundantes y podemos simplificarlas
como sigue:
fun(x : A.e) r se simplifica a e[x := r]
fsthe1 , e2 i se simplifica a e1 .
De la misma manera podemos justificar las siguientes simplificaciones
sndhe1 , e2 i se simplifica a e2 .
case (inl r) of inl x s | inr y t se simplifica a s[x := r]
case (inr r) of inl x s | inr y t se simplifica a t[x := r]
Estas reglas de simplificaci
on pueden utilizarse como reglas de evaluacion de las expresiones de
un lenguaje de programaci
on. En conclusion las pruebas redundantes de la logica y su simplificaci
on
corresponden a un proceso de evaluaci
on de expresiones en un lenguaje de programacion.
6.1.
Compilaci
on de expresiones aritm
eticas y booleanas
n s = (n:s)
true s = (t:s)
false s = (f:s)
+ (v1:v2:s) = (v1+v2) : s
* (v1:v2:s) = (v1*v2) : s
< (v1:v2:s) = (v1<v2) : s
not (v:s) = ( v) : s
iszero (v:s) = (isz v) : s
En realidad es una pila y elegimos implementarla como una lista, lo mismo sucede con la memoria
donde del lado derecho de las ecuaciones nos referimos a las operaciones binarias +, ::
N N N, <: N N B y a las operaciones unarias : B B, isz : N B
Ejecuci
on de un programa: ejecutar un programa p en la memoria dada s devuelve la memoria
obtenida al ejecutar en orden todas instrucciones de p.
ejp nil s = s
ejp (i:p) s = ejp p (ej i s)
Compilaci
on de una expresi
on en un programa: el proceso de compilacion convierte una expresion e en un programa.
comp
comp
comp
comp
comp
comp
comp
comp
n = [n]
true = [t]
false = [f]
(e1+e2) = comp e2 ++ comp e1 ++ [+]
(e1*e2) = comp e2 ++ comp e1 ++ [*]
(e1<e2) = comp e2 ++ comp e1 ++ [<]
(not e) = comp e ++ [not]
(iszero e) = comp e ++ [iszero]
donde +
+ es la funci
on de concatenacion de listas. Porque en los casos de operadores binarios
queda el programa resultado de compilar la segunda expresion e2 al inicio de la lista?
Correctud del compilador: la memoria resultado de ejecutar el programa p obtenido al compilar
la expresi
on e con la memoria vaca coincide con la memoria cuyo u
nico valor es la evaluaci
on
de la expresi
on e
e (ejp (comp e) nil = [eval e])
Por supuesto en todas las definiciones anteriores estamos suponiendo que las expresiones de
entrada son coherentes en el sentido de que cualquier operacion estara bien definida. Es decir,
estamos suponiendo que el proceso de verificacion previa fue hecho por el sistema de tipos.
Por lo que el cuantificador e se refiere u
nicamente a todas las expresiones semanticamente
correctas.
6.2.
Para probar la correctud del compilador vamos a probar algo mas general:
e p s ejp ( comp e
10
++
p) s =
ejp p (eval e:s)
de esta propiedad la correctud del compilador resulta un corolario tomando p = nil, s = nil.
La prueba es por inducci
on sobre las expresiones. Analizamos aqu un caso base y dos casos
inductivos:
Base e = n: P.D. ps(ejp (comp n ++ p) s = ejp p (eval n : s)).
ejp (comp n ++ p) s =
=
=
=
=
ejp ([n] ++ p) s
ejp (n : p) s
ejp p (ej n s)
ejp p (n : s)
ejp p (eval n : s)
Paso inductivo e = e1 e2 .
I.H.1.: ps(ejp (comp e1 +
+ p) s = ejp p (eval e1 : s))
I.H.2.: ps(ejp (comp e2 +
+ p) s = ejp p (eval e2 : s))
Queremos demostrar que:
ps(ejp (comp(e1 e2 ) ++ p) s = ejp p (eval(e1 e2 ) : s))
ejp (comp(e1 e2 ) +
+ p) s =
=
=Asoc ++
=I.H.2
=Asoc ++
=I.H.1
=
=
=
=
A continuaci
on damos una idea de la formalizacion de esta prueba en el sistema de deducci
on
natural.
11
6.3.
B
osquejo de la prueba formal (dentro del sistema de deducci
on natural)
=def
F consta de las cerraduras universales de las ecuaciones que definen a las funciones eval, ej, ejp, comp.
F = { n(eval n = n), . . . ,
e1 e2 eval(e1 + e2) = eval e1 + eval e2 , . . .
v1v2s ej < (v1 : v2 : s) = (v1 < v2) : s , . . .
..
.
e comp(iszero e) = comp e ++[iszero]
}
L consta de las propiedades requeridas de las operaciones de listas:
Concatenaci
on de lista unitaria: x`([x] ++ ` = x : `).
Asociatividad de la concatenacion:
xyz x ++(y ++ z) = (x ++ y) ++ z
E contiene los llamados axiomas de igualdad:
Reflexividad: x(x = x)
Simetra xy(x = y y = x)
Transitividad: xyz(x = y y = z x = z)
12
Sustituci
on (Leibniz): xy(x = y A(x) A(y)) donde A es una formula cualquiera3
Alternativamente en vez de usar estas formulas directamente para derivar nuevas formulas o
ecuaciones podemos utilizar la l
ogica ecuacional discutida antes, es decir, usar las siguientes
reglas:
`s=t
`s=t `t=r
(Ref l)
(Sim)
(T rn)
`t=t
`t=s
`s=r
` s = t ` A(s)
(Rewrite)
` A(t)
donde A es cualquier f
ormula.
A continuaci
on bosquejamos la prueba formal ` eP (e).
Para esto basta probar el antecedente del axioma de induccion IP y usar modus ponens.
Como dicho antecedente es una conjuncion basta probar cada parte por separado y usar la
regla (I).
Cada parte se prueba formalizando las pruebas informales, dado que el razonamiento ecuacional est
a permitido por las hip
otesis de E y por las propiedades de listas L . Como
ejemplo formalizemos el caso base para n
umeros naturales:
Base P.D. ` P (n), es decir, ` ps(ejp (comp n ++ p) s = ejp p (eval n : s)).
1.
2.
3.
4.
5.
6.
7.
8.
6.4.
`
`
`
`
`
`
`
`
ejp (comp n +
+ p) s = ejp (comp n ++ p) s
ejp (comp n +
+ p) s = ejp ([n] ++ p) s
ejp (comp n +
+ p) s = ejp (n : p) s
ejp (comp n +
+ p) s = ejp p (ej n s)
ejp (comp n +
+ p) s = ejp p (n : s)
ejp (comp n +
+ p) s = ejp p (eval n : s)
s ejp (comp n +
+ p) s = ejp p (eval n : s)
ps ejp (comp n ++ p) s = ejp p (eval n : s)
(Ref l)
(Rewrite) 1 def. comp n
(Rewrite) 2 prop. de listas
(Rewrite) 3 def. ejp
(Rewrite) 4 def. ej
(Rewrite) 5 def. eval
(I) 6, s
/ F V ()
(I) 7, p
/ F V ()
Para qu
e una prueba formal?
Una prueba formal como la que acabamos de bosquejar no es una argumentacion en alg
un
lenguaje natural, como el espa
nol, o bien semiformal como la mezcla de espa
nol y matematicas,
la cual es susceptible de errores dficiles de encontrar en casos de aplicacion real. Por ejemplo la
construccion de un compilador correcto (altamente confiable) de un lenguaje real como C (ver
http://compcert.inria.fr). Las pruebas formales constan de un proceso de calculo preciso que
sigue reglas bien definidas, en nuestro caso las reglas de deduccion natural. Una ventaja de construir
esta clase de pruebas es que debido a su naturaleza precisa son rigurosas, libres de ambig
uedades
y susceptibles de construirse y verificarse mecanicamente por programas llamados asistentes de
prueba como Coq (ver http://coq.inria.fr). Esta verificacion a la vez produce una certificaci
on
que garantiza que ciertas propiedades se cumplen, como por ejemplo la correctud de un compilador.
3
13
Esto es deseable en sistemas cuyo funcionamiento incorrecto causa errores graves como sistemas de
radiacion para enfermos de cancer y sistemas de navegacion de vuelo.
El uso de sistemas l
ogicos para desarrollar aplicaciones con propiedades certificadas mediante prueba
formales ha generado una nueva
area dentro de las ciencias de la computacion conocida como
Metodos Formales.
14