Documente Academic
Documente Profesional
Documente Cultură
actico Final
Paradigmas de Lenguajes de Programacion (1er cuatrimestre de 2009)
Integrante
LU
Correo electronico
Castillo, Gonzalo
164/06
gonzalocastillo 086@hotmail.com
Martnez, Federico
17/06
federicoemartinez@gmail.com
Sainz-Tr
apaga, Gonzalo
454/06
gonzalo@sainztrapaga.com.ar
Indice general
1. Introducci
on
1.1. Introducci
on te
orica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3. Organizaci
on del informe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2. Detalles de implementaci
on
2.2. Definici
on de extensiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.1. Creaci
on de Tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.2. Creaci
on de Expresiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.3. Construcci
on de extensiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3. Sintaxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3.1. Mecanismo de an
alisis sint
actico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3.2. Definici
on de reglas sint
acticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.4. Tipado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.5. Sem
antica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.6.2. Abstracciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.6.4. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.6.5. Aplicaci
on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.6.6. Nat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.6.7. Zero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.6.8. Succ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.6.9. Pred . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.6.10. isZero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3. Tutorial
18
3.1. La extensi
on Maybe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.2. C
odigo boilerplate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.3. Creando un nuevo tipo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.4. Definici
on de expresiones b
asicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.5. Definici
on de un termino derivado simple
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.6. Definici
on de un termino derivado complejo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.7. Creaci
on de la extensi
on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4. Extensiones
25
4.1. Tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
4.2. Expresiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
27
5.1. Subtipado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.2. Inferencia de tipos y otras alternativas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
6. Conclusi
on
30
Pagina 1 de 30
Parte 1
Introducci
on
1.1.
Introducci
on te
orica
El C
alculo Lambda es un modelo de computaci
on basado en funciones. Por tratarse de un modelo computacionalmente
completo y riguroso en su formulaci
on, se utiliza con frecuencia como herramienta para el estudio de lenguajes de programaci
on y sus propiedades, as como lenguaje de referencia para la definici
on de sem
antica en otros lenguajes de programaci
on.
La versi
on del C
alculo Lambda desarrollada en este trabajo es el C
alculo Lambda Tipado (A. Church, 19401 ).
1.2.
1.3.
Organizaci
on del informe
Pagina 2 de 30
Parte 2
Detalles de implementaci
on
2.1.
Lenguaje de programaci
on
Para la implementaci
on del interprete utilizamos el lenguaje de programaci
on Python, en parte por tratarse de un
lenguaje con el que estamos acostumbrados a trabajar, pero adem
as porque consideramos que su sintaxis sencilla y limpia
es adecuada para facilitar la construcci
on posterior de extensiones, a
un por personas que no esten familiarizadas con el
lenguaje.
2.2.
Definici
on de extensiones
2.2.1.
Creaci
on de Tipos
Para crear un nuevo tipo, se debe heredar de la clase Tipo y asegurarse de implementar los siguientes metodos:
init (self, tok): el constructor de una instancia de un tipo, que toma una lista de tokens (ver 2.3) y a partir
de estos construye la instancia. En general, al momento de ser procesados por este constructor, los tokens ya fueron
procesados y son objetos que representan a su vez a otros tipos (an
alogo al funcionamiento de un fold ), y son necesarios
para la creaci
on de tipos recursivos.
sintaxis(cls): un classmethod o metodo est
atico que define la sintaxis con la que se va a parsear una expresi
on de
tipo1 .
eq (self,other): la comparaci
on por igualdad entre tipos, que debe funcionar como la relaci
on unifica. Por defecto
la clase padre Tipo considera que un tipo unifica con otro si son de la misma clase, por lo cual si este comportamiento
es el esperado no es necesario definirlo (por ejemplo, Nat unifica con Nat, pero para tipos recursivos podra no ser
suficiente esta definici
on).
1 Nota: No confundir expresi
on de tipo con expresiones tipadas del c
alculo. La clase hija de Tipo define la sintaxis que utiliza el
lenguaje para referirse al tipo propiamente dicho, por ejemplo, en las declaraciones de funciones. La sintaxis de los t
erminos que tipan
a este tipo es competencia de las clases hijas de expresi
on, que se detallan m
as adelante
Pagina 3 de 30
str (self ): (optativo) el equivalente a toString en otros lenguajes, que se utiliza para imprimir por pantalla a las
instancias de la clase.
2.2.2.
Creaci
on de Expresiones
2.2.3.
Construcci
on de extensiones
2.3.
2.3.1.
Sintaxis
Mecanismo de an
alisis sint
actico
Pagina 4 de 30
una definici
on gramatical. Si bien algunos de los problemas de los parsers LL(1) se eliminan a costa de usar backtracking,
otros persisten: no es posible utilizar recursi
on a izquierda en la definici
on de reglas. Esta es una limitaci
on propia del
mecanismo de parsing que nos oblig
o a realizar algunas modificaciones en el lenguaje aceptado por el interprete.
As, por ejemplo, para el caso de la aplicaci
on, la producci
on:
Exp Exp Exp
no puede ser parseada por este procedimiento (dado que el programa entrara en un bucle infinito hasta terminar por stack
overflow. Por lo tanto, en su lugar proponemos:
Exp (Exp Exp)
La gram
atica sigue siendo ambigua, pero al eliminar la recursi
on a izquierda mediante la introducci
on de parentesis, el
parser puede eliminar la ambiguedad utilizando alguna heurstica de decisi
on (sea first-match o longest-match, seg
un lo que
uno considere m
as conveniente). Esto elimina una preocupaci
on a la hora de definir nuevas extensiones.
Por u
ltimo, es importante mencionar que las expresiones del lenguaje, a excepci
on de los terminos de tipo y las variables,
no deber
an utilizar letras may
usculas ya que este criterio se utiliza para diferenciar entre s a estos diferentes elementos.
2.3.2.
Definici
on de reglas sint
acticas
Como se mencion
o antes, es el metodo est
atico sintaxis() para expresiones el que devuelve el lado derecho de una
producci
on que se utilizar
a al momento de parsear una expresi
on. Este lado derecho se asocia al no terminal Exp de la
gram
atica, que es el smbolo inicial que produce todos los terminos del lenguaje, y puede ser usado en la definici
on de la
sintaxis para referirse (recursivamente) a cualquier termino. Su an
alogo TipoExp caracteriza a las expresiones de tipos y
puede a su vez usarse en las definiciones de sintaxis.
Para la definici
on de la sintaxis se pueden utilizar directamente las distintas clases que provee PyParsing, y sobre las
que hay excelente documentaci
on en el sitio web correspondiente. Por ejemplo, la sintaxis del if resultara:
Exp if Exp then Exp else Exp
Utilizando las clases de PyParsing:
Keyword(if ).suppress() + Exp + Keyword(then).suppress() + Exp + Keyword(else).suppress() + Exp
N
otese que la sintaxis es simple, utilizando el operando de suma para la concatenaci
on y las referencias a Exp cuando
es necesario indicar que en ese lugar aparece una expresi
on arbitraria. La lista de tokens que se construye a partir de esa
expresi
on es la que luego recibe el constructor de la expresi
on para instanciar un objeto.
El metodo suppress() elimina el token despues de hallarlo porque no resulta de interes para su utilizaci
on posterior
(puesto que no contiene informaci
on). As, se eliminan todos los strings fijos de la expresi
on conservando as solo las
subexpresiones, que son las que interesan al momento de construir una instancia del objeto correspondiente a la expresi
on
if. N
otese que no es necesario utilizar suppress(), pero de no hacerlo se deber
an ignorar los tokens en el constructor de la
expresi
on.
Sin embargo, para sintaxis simples puede usarse el helper expresion.makeSintaxis(*args), que recibe como par
ametros
los componentes sint
acticos para armar la producci
on. Asi por ejemplo, para definir la sintaxis del if:
makeSintaxis(if ,Exp,then,Exp,else,Exp)
La funci
on makeSintaxis transforma las cadenas de letras en Keywords, las cadenas que contengan smbolos en Literals, y
las Exp (o cualquier otra instancia de parseElement) las mantiene, y devuelve la expresi
on que resulta de la concatenaci
on
Pagina 5 de 30
2.4.
Tipado
Dado que implementamos un lambda calculo tipado, un elemento fundamental en la definicion de nuevas expresiones son
las reglas de tipado. El metodo debe devolver una instancia de alguna subclase de Tipo indicando el tipo correspondiente o
debe generar TypeException en caso de que el termino no tipe.
Para definir reglas de tipado se puede usar el = entre tipos, por esta raz
on es necesario que las subclases de Tipo
implementen un eq adecuado a su sem
antica.
La funci
on tipar recibe ademas como par
ametro el contexto de tipado, que funciona como diccionario de nombre de
variable (string) a su tipo.
2.5.
Sem
antica
La sem
antica en nuestra implemetanci
on est
a dada por el metodo reducir. Esta reducci
on es del tipo big-step, es decir
si un termino tipa, una llamada a reducir debe devolver el valor al que reduce.
A la hora de definir la sem
antica, puede asumirse como precondici
on que el termino tipa, ya que el interprete chequea
previamente que la expresi
on tipe antes de intentar reducirla. Es importante asegurarse de que si el termino tipa, la
reducci
on no deberia fallar. De esta manera el metodo reducir debe devolver la instancia de expresi
on que resulta de reducir
completamente al termino.
2.6.
Tipos y t
erminos b
asicos
En esta secci
on presentaremos los tipos b
asicos vistos en clase junto con su implementaci
on para nuestro lenguaje.
Consideraremos como tipos b
asicos a los booleanos, los naturales y a las funciones.
Es interesante notar que estos no son objetos privilegiados dentro de la jerarqua del interprete, y se definen exactamente
de la misma manera en que se definira cualquier extensi
on. Sin embargo, por su naturaleza esencial, son requeridos por casi
todas las dem
as extensiones y por tanto son de interes especial.
2.6.1.
T
erminos
Pagina 6 de 30
Reglas de tipado
. true : Bool
. f alse : Bool
Sem
antica
Las expresiones true y false son valores, no reducen.
Implementaci
on
Esta implementaci
on, por su sencillez, hace uso de otros dos helpers apropiados para la construcci
on de tipos b
asicos
(no parametricos) y a
tomos de alg
un tipo particular. Se trata de las funciones simpleTypeFactory (que crea una clase
apropiada a partir de la sintaxis del termino de tipo) y atomoFactory (que crea una clase hija de Expresion a partir de su
sintaxis simple y la clase correspondiente a su tipo).
Estas dos funciones se utilizan en varias ocasiones cuando es necesario definir casos sencillos, ahorrando as la repetici
on
innecesaria de c
odigo en los lugares donde no es necesaria la flexibilidad.
Boolean = simpleTypeFactory(Boolean)
Verdadero = atomoFactory(true, Boolean)
Falso = atomoFactory(false, Boolean)
2.6.2.
Abstracciones
T
erminos
M ::= ...|X : .M
Reglas de tipado
, X : . M :
. X : .M :
Sem
antica
Las abstracciones son valores, no reducen.
Implementaci
on
class Func(Tipo):
def
init (self,tok):
"""
Define para el tipo funcion el dominio y la imagen.
"""
dom, img = tok
self.dominio = dom
self.imagen = img
def
eq (self, other):
"""
Un tipo es igual a un tipo funcion, si es de tipo funcion
y sus dominios e imagenes son iguales.
"""
return self. class == other. class and \
self.dominio == other.dominio and \
self.imagen == other.imagen
@classmethod
def sintaxis(cls):
Abs = makeSintaxis((, TipoExp, -> , TipoExp, ) )
return Abs
def
str (self):
s1 = str(self.dominio)
s2 = str(self.imagen)
if self.dominio. class == self. class :
return "( %s) -> %s" % (s1, s2)
else:
return " %s -> %s" % (s1, s2)
class Abstraccion(Expresion):
def
def reducir(self):
"""
Una Abstraccion reduce a si misma, es un valor.
"""
return self
def tipar(self, namespace):
"""
gamma,{X:alpha} | > M : beta
---------------------------gamma | > \X:alpha.M : (alpha -> beta)
"""
nam2 = dict(namespace)
nam2[self.var] = self.tipoVar
return Func([self.tipoVar, self.expresion.tipar(nam2)])
Pagina 7 de 30
Pagina 8 de 30
2.6.3.
Id , :, TipoExp , ., Exp)
str (self):
return "\\ %s : %s . %s" % (self.var, self.tipoVar, self.expresion)
If Then Else
T
erminos
Reglas de tipado
. M : Bool, . P : , . Q :
. if M then P else Q :
Sem
antica
M M
if M then P else Q if M then P else Q
Implementaci
on
class IfThenElse(Expresion):
def
def reducir(self):
"""
Reducci
on de la guarda a un valor. Luego dependiendo de dicho valor
se reduce la rama verdadera o la falsa.
"""
if isinstance(self.guarda.reducir(), Verdadero):
return self.ramaTrue.reducir()
else:
return self.ramaFalse.reducir()
Pagina 9 de 30
Pagina 10 de 30
# if M then N else O
If = makeSintaxis(if , Exp,then, Exp,else, Exp)
return If
def
2.6.4.
str (self):
return "if %s then %s else %s" % (self.guarda, self.ramaTrue, self.ramaFalse)
Variables
T
erminos
M ::= ...|X
Reglas de tipado
{X : }
.X :
Sem
antica
Las variables no reducen, tienen que ser sustitudas.
Implementaci
on
class Variable(Expresion):
def
def reducir(self):
"""
Las variables reducen a si mismas, tiene que ser sustituidas.
"""
return self
def tipar(self, namespace):
"""
{X : alpha} pertenece a gamma
---------------------------gamma | > X : alpha
"""
if not (self.id in namespace):
for each in namespace:
Pagina 11 de 30
2.6.5.
str (self):
return self.id
Aplicaci
on
T
erminos
M ::= ...|M N
Reglas de tipado
. M : , . N :
.M N :
Sem
antica
M M
M N M N
N N
V N V N
(X : .M )V M {X V }
Pagina 12 de 30
Implementaci
on
class Aplicacion(Expresion):
def
def reducir(self):
"""
Se reduce la funcion aplicadora y el parametro, luego:
(\X:alpha.M V) --> M {X <- V}
Finalmente se sustituye M {X <- V} y se reduce M.
"""
aplicador = self.aplicador.reducir()
parametro = self.parametro.reducir()
res = aplicador.expresion.sustituir(aplicador.var, parametro)
return res.reducir()
else:
raise TypeException((Tipo del aplicador: %s \n+ \
El tipo del aplicador no se corresponde con el tipo de una Abstraccion.) %
x)
def sustituir(self, var, expresion):
"""
(M N) {X <- T} --> (M {X <- T} N {X <- T})
"""
aplicador = self.aplicador.sustituir(var, expresion)
parametro = self.parametro.sustituir(var, expresion)
return Aplicacion([aplicador, parametro])
@classmethod
def sintaxis(cls):
App = makeSintaxis((, Exp, White().suppress(), Exp,))
return App
def
2.6.6.
Pagina 13 de 30
str (self):
return "( %s %s)" % (str(self.aplicador), str(self.parametro))
Nat
Implementaci
on
Nat = simpleTypeFactory(Nat)
2.6.7.
Zero
T
erminos
M ::= ...|zero
Reglas de tipado
. zero : N at
Sem
antica
zero no reduce, es un valor.
Implementaci
on
2.6.8.
Succ
T
erminos
M ::= ...|succ(M )
Pagina 14 de 30
Reglas de tipado
. M : N at
. succ(M ) : N at
Sem
antica
M M
succ(M ) succ(M )
Implementaci
on
class Succ(Expresion):
def
def reducir(self):
"""
Reducir succ(E) corresponde a reducir E.
"""
terminoRed = self.termino.reducir()
return Succ([terminoRed])
def tipar(self, namespace):
"""
gamma | > E : Nat
----------------gamma | > succ(E) : Nat
"""
if self.termino.tipar(namespace) == Nat([]):
return Nat([])
raise TypeException((El tipo %s de la expresion: %s \n+ \
no se corresponde con el tipo Nat.) %
(self.termino.tipar(namespace), self.termino))
def sustituir(self, var, expresion):
"""
succ(E) {X <- T} --> succ(E {X <- T})
"""
return Succ([self.termino.sustituir(var, expresion)])
@classmethod
def sintaxis(cls):
succ = makeSintaxis(succ(, Exp, ))
return succ
def
str (self):
Pagina 15 de 30
2.6.9.
Pred
T
erminos
M ::= ...|pred(M )
Reglas de tipado
. M : N at
. pred(M ) : N at
Sem
antica
M M
pred(M ) pred(M )
pred(succ(M )) M
pred(zero) zero
Implementaci
on
class Pred(Expresion):
def
def reducir(self):
"""
Se reduce primero el la expresion que contiene pred, luego:
pred(zero) --> zero
pred(succ(v)) --> v
"""
Pagina 16 de 30
termino = self.termino.reducir()
if isinstance(termino, Zero):
return termino
else:
return termino.termino
def tipar(self, namespace):
"""
gamma | > E : Nat
----------------gamma | > pred(E) : Nat
"""
if self.termino.tipar(namespace) == Nat([]):
return Nat([])
raise TypeException((El tipo %s de la expresion: %s \n+\
no se corresponde con el tipo Nat.) %
(self.termino.tipar(namespace), self.termino))
def sustituir(self, var, expresion):
"""
pred(E) {X <- T} --> pred(E {X <- T})
"""
return Pred([self.termino.sustituir(var, expresion)])
@classmethod
def sintaxis(cls):
pred = makeSintaxis(pred(, Exp, ))
return pred
def
str (self):
return "pred( %s)" % self.termino
2.6.10.
isZero
T
erminos
M ::= ...|isZero(M )
Reglas de tipado
. M : N at
. isZero(M ) : Bool
Sem
antica
M M
isZero(M ) isZero(M )
Pagina 17 de 30
isZero(succ(M )) f alse
isZero(zero) true
Implementaci
on
class IsZero(Expresion):
def
def reducir(self):
"""
Se reduce primero la expresion que contiene isZero, luego:
isZero(zero) --> true
isZero(succ(v)) --> false
"""
termino = self.termino.reducir()
if isinstance(termino, Zero):
return Verdadero([])
else:
return Falso([])
def tipar(self, namespace):
"""
gamma | > E : Nat
---------------gamma | > isZero(E): Boolean
"""
if self.termino.tipar(namespace) == Nat([]):
return Boolean([])
else:
raise TypeException((El tipo %s de la expresion %s no se corresponde con el tipo Nat.) \
% (self.termino.tipar(namespace), self.termino))
def sustituir(self, var, expresion):
"""
isZero(E) {X <- T} --> isZero(E {X <- T})
"""
return IsZero([self.termino.sustituir(var, expresion)])
@classmethod
def sintaxis(cls):
isZ = makeSintaxis(isZero(, Exp, ))
return isZ
def
str (self):
return "isZero( %s)" % self.termino
Pagina 18 de 30
Parte 3
Tutorial
3.1.
La extensi
on Maybe
3.2.
C
odigo boilerplate
#!/usr/bin/python
# -*- coding: utf8 -*from expresion import Expresion, Tipo, Exp, TipoExp, TypeException
from expresion import makeSintaxis
from booleans import Boolean
from lenguaje import Extension
Como ver
an, se trata de una serie de imports de objetos que vamos a necesitar referenciar en nuestro c
odigo. Expresion
y Tipo son las clases de las que vamos a heredar para construir los terminos y tipos de nuestra extensi
on (que se construye
a partir de la clase Extension, que importamos al final de todo).
Exp y TipoExp son los patrones de sintaxis de PyParsing que podemos usar al definir sintaxis para referirnos a una
expresi
on cualquiera o n termino de tipo cualquiera. TypeException es la excepci
on que debemos producir cuando haya
un error de tipos
Finalmente, Boolean es la clase que corresponde al tipo hom
onimo (y por tanto vive en la extensi
on correspondiente). Es
Pagina 19 de 30
necesaria porque m
as adelante vamos a necesitar indicarle al sistema de tipado que un termino es de tipo Boolean (adivinan
cual?), y para esto es necesario instanciar dicha clase.
3.3.
En este caso no se puede utilizar el helper simpleTypeFactory porque este no es un tipo simple: se trata de un tipo
parametrico, y por lo tanto hay que definirlo a mano extendiendo directamente la clase Tipo. Comencemos entonces por
definir la sintaxis:
#######################################
# Declaraci
on del nuevo tipo Maybe(T) #
#######################################
class TipoMaybe(Tipo):
@classmethod
def sintaxis(cls):
return makeSintaxis(Maybe(, TipoExp, ))
Esta declaraci
on decorada (la lnea que comienza con @ se llama decorator en Python) crea un metodo est
atico de
la clase TipoMaybe (que luego se llama como TipoMaybe.sintaxis()). En este caso utilizamos el helper makeSintaxis para
decirle al interprete que un termino que describe al tipo TipoMaybe tiene la sintaxis Maybe(TipoExp), donde TipoExp
representa a una expresi
on de tipo cualquiera (por ejemplo, Nat, Bool o incluso Maybe(Nat)!).
La definici
on sin makeSintaxis hubiera sido m
as larga y difcil de leer(y adem
as hubiera sido necesario importar Literal
desde pyparsing!):
def
La construcci
on es simple, u
nicamente se almacena como un atributo de instancia el primer elemento de la lista toks,
que corresponde al primer y u
nico token que matchea la sintaxis del tipo (recordemos que como se utiliza suppress()
implcitamente sobre los chunks Maybe( y ), estos no ser
an recibidos por el constructor). Sigamos:
def
eq (self, otro):
return self. class == otro. class
self.tipo == otro.tipo
and \
str (self):
return Maybe( %s)" % self.tipo
Finalmente, el u
ltimo metodo define la forma en que se convierte el objeto a una representaci
on en forma de string
amigable para el usuario. En este caso (y en todos los dem
as), nos limitamos a reproducir la sintaxis.
Con esto ya tenemos el nuevo tipo definido, y podemos dedicarnos a construir los terminos asociados al mismo.
3.4.
Pagina 20 de 30
Definici
on de expresiones b
asicas
A continuaci
on vamos a definir las dos expresiones b
asicas del tipo Maybe(T), empezando por Nothing (y esta vez
heredando de Expresion en lugar de Tipo).
######################################
# Declaraci
on de nuevas expresiones #
######################################
class Nothing(Expresion):
@classmethod
def sintaxis(cls):
return makeSintaxis(nothing(, TipoExp, ))
La definici
on de sintaxis es an
aloga a la definici
on de sintaxis en el termino de tipo. Hay dos particularidades para notar
en esta definici
on. La primera es que la expresi
on que definimos como nothing(T) consta de una variable de tipo(denotada
por TipoExp) que permite tipar la expresi
on. En segundo lugar, no hay que olvidar que los terminos no deben llevar
may
usculas (ya que el parser asume que las cosas que comienzan por may
usculas dentro de una expresi
on son variables).
Por esta raz
on, el a
tomo creado se llama nothing(T) y no Nothing(T).
def
def
str (self):
return nothing( %s) % self.tipo
La funci
on reducir() es responsable de producir un termino reducido (con sem
antica big-step) a partir de la expresi
on
actual. Sin embargo, en el caso de Nothing y por tratarse de un valor, no hay que hacer ninguna reducci
on y es suficiente
con devolver una referencia al propio objeto que recibe el mensaje.
def tipar(self, namespace):
return TipoMaybe([self.tipo])
La funci
on tipar() se encarga de producir una instancia del tipo correspondiente a esta expresi
on. Para esto, nos servimos
de la clase que definimos anteriormente que instanciamos parametrizada en el tipo que parseamos a partir de la sintaxis.
As, convertimos nothing(Nat) en una instancia de T ypeM aybe < N at >.
N
otese la peculiaridad de que TipoMaybe recibe una lista de tokens, y por lo tanto se debe pasar como par
ametro
[self.tipo] (entre corchetes).
def sustituir(self, var, expresion):
return self
Pagina 21 de 30
class Just(Expresion):
@classmethod
def sintaxis(cls):
return makeSintaxis(just(, Exp, ))
def
def
str (self):
return just( %s) % self.exp
def reducir(self):
e = self.exp.reducir()
return Just([e])
La definici
on de reducci
on se ajusta a lo definido por el C
alculo Lambda: si M M luego Just(M) Just(M). Esto
es precisamente lo que se establece aqu, con una llamada recursiva a reducir() de la expresi
on hija.
def tipar(self, namespace):
return TipoMaybe([self.exp.tipar(namespace)])
def sustituir(self, var, expresion):
return Just([self.exp.sustituir(var, expresion)])
Estos dos u
ltimos casos son an
alogos al anterior: la implementaci
on no hace m
as que llamar recursivamente a las funciones
apropiadas en la subexpresi
on y construir a partir de dichos resultados los datos que le fueron pedidos.
Con esta u
ltima clase disponemos ya de los terminos b
asicos que constituyen al tipo parametrico MaybeT. Sin embargo,
para su uso posterior ser
a conveniente declarar algunos terminos adicionales que faciliten su utilizaci
on.
3.5.
Definici
on de un t
ermino derivado simple
A continuaci
on nos concierne la declaraci
on de la expresi
on isNothing(E) que reduce a true si E es Nothing, o a false si
E es Just J. Veamos su implementaci
on:
Pagina 22 de 30
class IsNothing(Expresion):
@classmethod
def sintaxis(cls):
return makeSintaxis(isNothing(, Exp, ))
def
def
str (self):
return isNothing( %s) % self.exp
def reducir(self):
e = self.exp.reducir()
if isinstance(e,Nothing):
return Verdadero([])
else:
return Falso([])
def sustituir(self, var, expresion):
return IsNothing([self.exp.sustituir(var, expresion)])
Observando la l
ogica del c
odigo, vemos que lo que hace es tipar el subtermino (es decir, si estamos lidiando con isNothing(E), obtiene el tipo de E) y a continuaci
on se fija si dicho tipo es realmente Maybe, verificando si es una instancia de
TipoMaybe. En caso afirmativo, el tipo de la expresi
on isNothing ser
a Boolean, clase de la que se devuelve una instancia.
De otro modo, se enva una TypeException indicando de forma informativa el error que se produjo, cual era el tipo esperado
y cual el que se recibi
o.
3.6.
Definici
on de un t
ermino derivado complejo
Como u
ltima declaraci
on analizaremos la implementaci
on de CaseMaybe, el observador en base a constructores de las
instancias de M aybe < T >.
class CaseMaybe(Expresion):
@classmethod
def sintaxis(cls):
return makeSintaxis(caseMaybe(, TipoExp, ), Exp, ; of nothing -> , Exp, ; just(J) -> , Exp)
def
def
str (self):
return caseMaybe( %s) %s; of nothing -> %s; just(J) -> %s % \
(self.tipo, self.exp, self.resNothing, self.resJust)
Hasta este punto no se observan diferencias significativas. Es de notar que para simplificar el ejemplo se utiliz
o el nombre
Pagina 23 de 30
La implementaci
on de la regla de tipado es similar a la que vimos en isNothing, aunque el chequeo a realizar es m
as
complejo (ya que deben asegurarse m
as condiciones). En este caso, se verifica que ambas ramasdel case tengan el mismo
tipo despues de ligar la variable J en el caso de Just, as como que self.exp tenga el tipo apropiado seg
un la declaraci
on del
case.
Una peculiaridad es el uso del namespace: n
otese que a diferencia de los casos anteriores, el contenido del mismo se
modifica. El namespace es un diccionario nativo de Python, que mapea strings a instancias de subclases de Tipo. Dado
que los diccionarios (al igual que pr
acticamente todo en Python) se pasan por referencia, es necesario copiar el namespace
explcitamente antes de realizar la modificaci
on del mismo ya que de otro modo se corre el riesgo de contaminar el namespace
de otras expresiones que compartan la misma instancia del diccionario.
def sustituir(self, var, expresion):
sMaybe = self.exp.sustituir(var, expresion)
sresNothing = self.resNothing.sustituir(var, expresion)
if var != J:
sresJust = self.resJust.sustituir(var, expresion)
else:
sresJust = self.resJust
return CaseMaybe([self.tipo, sMaybe, sresNothing, sresJust])
Por u
ltimo, el mecanismo de sustituci
on tambien es m
as complejo: en este caso, y como hay una variable ligada por esta
expresi
on, debe asegurarse de que no se realice una sustituci
on de la misma por pedido de una expresi
on de nivel m
as alto.
En este punto se establece el scope de la variable J, y se determina que su valor dentro del bloque correspondiente
Pagina 24 de 30
3.7.
Creaci
on de la extensi
on
Finalmente, el u
nico punto pendiente es la creaci
on de la extensi
on propiamente dicha. El c
odigo es autoexplicativo: no
hay m
as que instanciar la clase Extension con una etiqueta apropiada.
######################################
# Armado de la nueva extensi
on
#
######################################
extensionMaybe = Extension(Maybe, expresiones=[Just, Nothing, IsNothing, CaseMaybe], tipos=[TipoMaybe])
Por u
ltimo, para que la extensi
on sea detectada por los aplicativos (tanto de consola como la interfaz gr
afica, es necesario
agregarla a la lista extensiones en registro.py. Esta lista es consultada por las aplicaciones para conocer las extensiones
que est
an disponibles.
Parte 4
Extensiones
Los siguientes son los tipos y expresiones definidos por nosotros a modo de librera.
4.1.
Tipos
Boolean
Natural
Funci
on ( )
Registro
Lista
Arbol
Binario
Maybe
Tupla (Par ordenado)
4.2.
Expresiones
Booleanos:
True
False
if M then P else Q
Naturales:
zero
succ(M)
pred(M)
isZero(M)
Funciones:
Variables
X : T.M
Aplicaci
on (M N)
Registros:
construcci
on de registros
proyecci
on de registros
Listas:
Lista vaca
agregar elemento adelante (add(E,L))
case para listas (esquema para pattern matching)
Pagina 25 de 30
Arboles binarios:
nil
constructor de a
rbol binario (bin(M,N,O))
case para arboles
Maybe
Nothing
Just
isNothing
case para Maybe
Tuplas
constructor de tupla
proyectores
Pagina 26 de 30
Pagina 27 de 30
Parte 5
Subtipado
Una ampliaci
on posible para el trabajo sera la incorporaci
on de reglas de subtipado. Esto es factible dentro del esquema
propuesto, y la forma simple de hacerlo sera sobrecargar el operador < para los tipos. De este modo, en vez de preguntar
si un tipo es igual a otro, se pregunta por menor o igual al momento de realizar los chequeos de tipos.
El siguiente c
odigo implementa la extensi
on de registros:
class TypeReg(Type):
"""
El tipo registro estara compuestos por etiquetas con tipos asociados.
"""
def init (self, tok ):
i =0
ids = {}
while(i < len(tok)):
ids[tok[i]] = tok[i+1]
i =i +2
self.ids = ids
"""
La igualdad de un tipo, con un tipo registro se da si en principio es un
registro y sus etiquetas coinciden en todos los tipos con los del otro tipo registo.
"""
def eq (self, otro):
if isinstance(otro, TypeReg):
res = True
for each in self.ids:
res = res and each in otro.ids and self.ids[each] == otro.ids[each]
if(not res):
return res
return len(self.ids) == len(otro.ids)
else:
return False
@classmethod
def sintaxis(cls):
campo = makeSintaxis(Id, :, TypeExp)
listaCampos = Optional(campo + ZeroOrMore(makeSintaxis(",", campo)))
Pagina 28 de 30
str (self):
s = {
primero = True
for each in self.ids:
if primero:
primero = False
s+= str(each)+:+str(self.ids[each])
else:
s+=,+str(each)+:+str(self.ids[each])
s += }
return s
En el caso de los registros, tenemos reglas de subtipado a lo ancho y a lo largo, que podemos escribir como:
5.2.
Pagina 29 de 30
Pagina 30 de 30
Parte 6
Conclusi
on
Como conclusi
on final podemos enumerar una serie de conceptos que resultaron de la realizaci
on de este trabajo pr
actico.
En primer lugar, y como se explic
o en la introducci
on, la idea de este trabajo era poder lograr un interprete de lambda
c
alculo que no s
olo funcionara de manera correcta, sino que tambien pudiese ser extendido para incluir nuevos tipos y
expresiones de forma sencilla.
Consideramos que este objetivo se cumpli
o, ya que pudimos elaborar una manera est
andar de introducir nuevas extensiones al interprete. Adem
as, consideramos que hallamos un equilibrio razonable entre la sencillez de extensi
on y la flexibilidad
que se permite al agregar nuevos terminos al lenguaje. No se utilizaron conveciones extremadamente fuertes que podran
haber causado limitaciones poco pr
acticas para extender el lenguaje de forma extravagante. A su vez, la inclusi
on del tutorial
debera hacer que la incorporaci
on de una nueva extensi
on sea un procedimiento sencillo incluso para aquellas personas que
no est
an familiarizadas con el trabajo ni con el lenguaje Python.
Por otro lado creemos que el enfoque del trabajo se orient
o a uno de los temas m
as importantes de la cursada, y que se
logr
o no s
olo combinarlo con programaci
on orientada a objetos, sino tambien encontrar una forma din
amica de resolverlo. Si
bien es cierto que durante la cursada se implementaron cuestiones relacionadas al lambda c
aculo, la visi
on de este trabajo es
m
as compacta y abarca las cuestiones principales del tema (como ser el manejo de tipado, sintaxis, reducci
on y extensiones
al c
alculo). Adem
as, los trabajos pr
acticos llevados a cabo durante la cursada tuvieron enfoques menos globales que este,
que nos permiti
o construir un interprete de un lenguaje de programaci
on de principio a fin.
Como se
nalamos en el apartado anterior, nos hubiese gustado tambien implementar otras cuestiones como es el caso de
las reglas de subtipado. Sin embargo, se dejaron las puertas abiertas a dicha extensi
on como se coment
o anteriormente.
Consideramos que como punto final a la materia, la realizaci
on de un trabajo pr
actico constituy
o una soluci
on din
amica e
interactiva, y nos permiti
o utilizar lo aprendido para construir una herramienta completa, que podra tener posibilidades de
uso futuro dentro de la materia. Futuros estudiantes pueden examinar el c
odigo entregado para comprender el funcionamiento
de un interprete, y servirse de la gran cantidad de extensiones propuestas como referencia para implementar las suyas.