Interprete Calculo Lambda
-
Upload
carlos-antonio-lazaro-mauricio -
Category
Documents
-
view
228 -
download
0
Transcript of Interprete Calculo Lambda
-
7/23/2019 Interprete Calculo Lambda
1/33
Trabajo Practico Final
Paradigmas de Lenguajes de Programacion (1er cuatrimestre de 2009)
Integrante LU Correo electronico
Castillo, Gonzalo 164/06 gonzalocastillo [email protected]
Martnez, Federico 17/06 [email protected]
Sainz-Trapaga, Gonzalo 454/06 [email protected]
En el siguiente trabajo se presenta la implementacion de un interprete extensible para Calculo Lambda tipado. A lo largodel mismo se desarrollan las principales problematicas de implementacion, se explica como realizar nuevas extensiones alcalculo y se detallan posibles extensiones al trabajo.
Facultad de Ciencias Exactas y Naturales
Universidad de Buenos Aires
Ciudad Universitaria - (Pabellon I/Planta Baja)
Intendente Guiraldes 2160 - C1428EGA
Ciudad Autonoma de Buenos Aires - Rep. Argentina
Tel/Fax: (54 11) 4576-3359http://www.fcen.uba.ar
-
7/23/2019 Interprete Calculo Lambda
2/33
Indice general
1. Introduccion 1
1.1. Introduccion teorica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2. Objetivos del trabajo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3. Organizacion del informe. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2. Detalles de implementacion 2
2.1. Lenguaje de programacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2. Definicion de extensiones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2.1. Creacion de Tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2.2. Creacion de Expresiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.2.3. Construccion de extensiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.3. Sintaxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.3.1. Mecanismo de analisis sintactico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.3.2. Definicion de reglas sintacticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.4. Tipado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.5. Semantica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.6. Tipos y terminos basicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.6.1. Boolean, true y false . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.6.2. Abstracciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.6.3. If Then Else. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.6.4. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.6.5. Aplicacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.6.6. Nat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.6.7. Zero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.6.8. Succ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
-
7/23/2019 Interprete Calculo Lambda
3/33
2.6.9. Pred . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.6.10. isZero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3. Tutorial 18
3.1. La extension Maybe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.2. Codigo boilerplate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.3. Creando un nuevo tipo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.4. Definicion de expresiones basicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.5. Definicion de un termino derivado simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.6. Definicion de un termino derivado complejo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.7. Creacion de la extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4. Extensiones 25
4.1. Tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
4.2. Expresiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5. Posibles extensiones al trabajo 27
5.1. Subtipado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.2. Inferencia de tipos y otras alternativas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
6. Conclusion 30
-
7/23/2019 Interprete Calculo Lambda
4/33
Castillo, Martnez, Sainz-Trapaga Pagina 1 de30
Parte 1
Introduccion
1.1. Introduccion teorica
El Calculo Lambda es un modelo de computacion basado en funciones. Por tratarse de un modelo computacionalmentecompleto y riguroso en su formulacion, se utiliza con frecuencia como herramienta para el estudio de lenguajes de programa-cion y sus propiedades, as como lenguaje de referencia para la definicion de semantica en otros lenguajes de programacion.
La version del Calculo Lambda desarrollada en este trabajo es el Calculo Lambda Tipado (A. Church, 19401).
1.2. Objetivos del trabajo
El trabajo propuesto consiste en el desarrollo de un interprete para Calculo Lambda Tipado, con el foco fijado en lafacilidad de extension del mismo. Durante el cuatrimestre se utilizo una definicion basica del Calculo que se extendio pro-gresivamente agregando nuevas funcionalidades propias de los distintos paradigmas de programacion. La idea del interpretedesarrollado era adaptarse al desarrollo propuesto en la materia, de modo que agregar nuevas extensiones al calculo sea unatarea tan simple como sea posible.
La facilidad de extension se da a traves del uso del paradigma orientado a objetos y el uso intensivo de herencia comoherramienta de reuso de codigo, minimizando as la cantidad de lneas necesarias para la creacion de una extension. Dadaesta facilidad, debera ser factible incorporar al trabajo como complemento de la materia por la capacidad que provee depermitir a los alumnos interactuar de forma mas directa con el modelo.
Finalmente, ademas del sistema basico y facilmente extensible, se incluyen ademas con el trabajo la mayora de lasextensiones que fueron propuestas en clase durante el cuatrimestre, y documentacion suficiente como para que agregarnuevas extensiones sea una tarea accesible.
1.3. Organizacion del informe
El presente informe se organiza de la siguiente manera:
En el apartado2 se explican los detalles concernientes a la implementacion del interprete, as como las decisiones dediseno que fueron tomadas a la hora de desarrollar.
En 3 se detalla paso a paso la construccion de una nueva extension, con el objetivo de familiarizar al lector con elproblema y la solucion como la proponemos.
En4 se enumeran las extensiones ya implementadas por nosotros.
Por ultimo, en5 comentamos posibles extensiones o mejoras que podran realizarse al traba jo.
1A. Church: A Formulation of the Simple Theory of Types, JSL 5, 1940
-
7/23/2019 Interprete Calculo Lambda
5/33
Castillo, Martnez, Sainz-Trapaga Pagina 2 de30
Parte 2
Detalles de implementacion
2.1. Lenguaje de programacion
Para la implementacion del interprete utilizamos el lenguaje de programacion Python, en parte por tratarse de unlenguaje con el que estamos acostumbrados a trabajar, pero ademas porque consideramos que su sintaxis sencilla y limpiaes adecuada para facilitar la construccion posterior de extensiones, aun por personas que no esten familiarizadas con ellenguaje.
2.2. Definicion de extensiones
Conceptualmente, el interprete concibe a un lengua je como una coleccion de extensiones. Una extension consta de un
nombre apropiado para identificarla, una coleccion de tipos y una coleccion de terminos, as como las reglas necesarias paradarles tipado, sintaxis y semantica a las expresiones del lenguaje extendido.
Esta idea reproduce la aproximacion ofrecida en clase, en la que se parte de un c alculo basico con algunos elementosesenciales, y sobre el que se van construyendo nuevas estructuras.
Como dijimos, la caracterstica principal de esta implementacion es que permite agregar extensiones (es decir, tipos yexpresiones). As, en lo que respecta al codigo, crear nuevas extensiones consiste en subclasear dos clases llamadas Expresiony Tipo que residen en expresion.py.
2.2.1. Creacion 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 partirde estos construye la instancia. En general, al momento de ser procesados por este constructor, lostokens ya fueronprocesados y son objetos que representan a su vez a otros tipos (analogo al funcionamiento de unfold), y son necesariospara la creacion de tipos recursivos.
sintaxis(cls): un classmethod o metodo est atico que define la sintaxis con la que se va a parsear una expresion detipo1.
eq (self,other): la comparacion por igualdad entre tipos, que debe funcionar como la relacion unifica. Por defectola clase padre Tipo considera que un tipo unifica con otro si son de la misma clase, por lo cual si este comportamientoes el esperado no es necesario definirlo (por ejemplo, Nat unifica con Nat, pero para tipos recursivos podra no sersuficiente esta definicion).
1Nota: No confundir expresion de tipo con expresiones tipadas del calculo. La clase hija de Tipo define la sintaxis que utiliza el
lenguaje para referirse al tip o propiamente dicho, por ejemplo, en las declaraciones de funciones. La sintaxis de los terminos que tipan
a este tipo es competencia de las clases hijas de expresion, que se detallan mas adelante
-
7/23/2019 Interprete Calculo Lambda
6/33
Castillo, Martnez, Sainz-Trapaga Pagina 3 de30
str (self): (optativo) el equivalente a toStringen otros lenguajes, que se utiliza para imprimir por pantalla a lasinstancias de la clase.
2.2.2. Creacion de Expresiones
Para crear una nueva expresion valida dentro del lenguaje, se debe heredar de la clase Expresion y asegurarse deimplementar los siguientes metodos:
init (self, tok): el constructor, que construye una instancia de la expresion a partir de una lista de tokens (ver2.3).
sintaxis(cls): analogo a la extension de tipos, este metodo estatico define la sintaxis de una expresion de esta clase(ver 2.3).
reducir(self): implementa la semantica de la expresion (ver2.5).
tipar(self, namespace): implementa las reglas de tipado de la expresion (ver2.4).
sustituir(self, var, expresion): implementa las reglas de sustitucion para las instancias, dondevares el nombre deuna variable a ser reemplazada por expresion. Es aqu donde se determina, entre otras cosas, elscopede las variables.
str (self): (optativo) el equivalente a toStringen otros lenguajes, que se utiliza para imprimir por pantalla a lasinstancias de la clase.
2.2.3. Construccion de extensiones
Finalmente, una extension se modela como una instancia de la clase Extension, que se construye a partir de una etiqueta,una lista de expresiones y una lista de tipos. Los elementos de la listas son las clases que mencionamos previamente. As, puedeconstruirse un lenguaje a partir de una serie de extensiones que se cargan o descargan dinamicamente. El procedimiento sedetalla con mayor precision en3.
2.3. Sintaxis
2.3.1. Mecanismo de analisis sintactico
Uno de los problemas principales a resolver para permitir la posterior definicion de extensiones era como facilitar ladefinicion de la sintaxis de los nuevos terminos sin obligar al usuario a interactuar de forma directa con elparser utilizado.El desafo consistio ademas en dar una forma sencilla de definir la sintaxis, pero sin imponer restricciones innecesarias a lacomplejidad de las extensiones que p odran definirse.
Las herramientas que conocemos que permiten definir sintaxis y semantica de forma combinada y sin demasiado codigode por medio son las de Gramaticas de Atributos y Traduccion Dirigida por Sintaxis. Sin embargo, consideramos que lidiar
con las particularidades de las gramaticas libres de contexto era algo a evitar, por lo que buscamos una alternativa massimple.
La solucion elegida es intermedia. Por un lado, se logro que definir la sintaxis (y su correspondiente semantica) searelativamente sencillo, abstrayendo la mayor parte del codigo comun, y manteniendo una sintaxis medianamente simple. Porotra parte, varios de loshelpersintroducidos para simplificar la creacion de extensiones pueden omitirse para as tener masflexibilidad a la hora de trabajar. Esto se debe a que dichos helpersse definen en su mayora como funciones auxiliares queestan debilmente acopladas al resto del interprete, siendo as su uso totalmente optativo.
El parser elegido corresponde a la familia de parsers recursivos descendentes, y utilizamos la implementacion de la libreraPyParsingpor el uso excelente que hace de la sobrecarga de operadores y de las capacidades de alto nivel de Python parapermitir la definicion amigable de reglas gramaticales utilizando Python de forma directa, sin recurrir a definicies externasen otros lenguajes.
El parser utiliza backtracking, porque de otro modo sera imposible permitir anadir de forma simple nuevas reglassintacticas que podran introducir ambiguedades en parsers recursivos. Este problema proviene del hecho de que los parsersrecursivos descendentes son una familia similar a los LL(1), y por tanto lidian de forma limitada con los posibles prefijos de
-
7/23/2019 Interprete Calculo Lambda
7/33
Castillo, Martnez, Sainz-Trapaga Pagina 4 de30
una definicion 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 recursion a izquierda en la definicion de reglas. Esta es una limitacion propia delmecanismo de parsing que nos obligo a realizar algunas modificaciones en el lenguaje aceptado por el interprete.
As, p or ejemplo, para el caso de la aplicacion, la produccion:
Exp Exp Exp
no puede ser parseada por este procedimiento (dado que el programa entrara en un bucle infinito hasta terminar porstackoverflow. Por lo tanto, en su lugar proponemos:
Exp (Exp Exp)
La gramatica sigue siendo ambigua, pero al eliminar la recursion a izquierda mediante la introduccion de parentesis, elparser puede eliminar la ambiguedad utilizando alguna heurstica de decision (seafirst-match o longest-match, segun lo queuno considere mas conveniente). Esto elimina una preocupacion a la hora de definir nuevas extensiones.
Por ultimo, es importante mencionar que las expresiones del lenguaje, a excepcion de los terminos de tipo y las variables,no deberan utilizar letras mayusculas ya que este criterio se utiliza para diferenciar entre s a estos diferentes elementos.
2.3.2. Definicion de reglas sintacticas
Como se menciono antes, es el metodo estatico sintaxis() para expresiones el que devuelve el lado derecho de unaproduccion que se utilizara al momento de parsear una expresion. Este lado derecho se asocia al no terminal Exp de lagramatica, que es el smbolo inicial que produce todos los terminos del lenguaje, y puede ser usado en la definicion de lasintaxis para referirse (recursivamente) a cualquier termino. Su analogo TipoExp caracteriza a las expresiones de tipos ypuede a su vez usarse en las definiciones de sintaxis.
Para la definicion de la sintaxis se pueden utilizar directamente las distintas clases que provee PyParsing, y sobre lasque hay excelente documentacion en el sitio web correspondiente. Por ejemplo, la sintaxis delif 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
Notese que la sintaxis es simple, utilizando el operando de suma para la concatenacion y las referencias a Exp cuandoes necesario indicar que en ese lugar aparece una expresion arbitraria. La lista de tokens que se construye a partir de esa
expresion es la que luego recibe el constructor de la expresion para instanciar un objeto.
El metodo suppress() elimina el token despues de hallarlo porque no resulta de interes para su utilizacion posterior(puesto que no contiene informacion). As, se eliminan todos los strings fijos de la expresion conservando as solo lassubexpresiones, que son las que interesan al momento de construir una instancia del objeto correspondiente a la expresionif. Notese que no es necesario utilizarsuppress(), pero de no hacerlo se deberan ignorar los tokensen el constructor de laexpresion.
Sin embargo, para sintaxis simples puede usarse elhelperexpresion.makeSintaxis(*args), que recibe como parametroslos componentes sintacticos para armar la produccion. Asi por ejemplo, para definir la sintaxis del if:
makeSintaxis(if,Exp,then,Exp,else,Exp)
La funcion makeSintaxis transforma las cadenas de letras en Keywords, las cadenas que contengan smbolos en Literals, ylas Exp (o cualquier otra instancia deparseElement) las mantiene, y devuelve la expresion que resulta de la concatenacion
-
7/23/2019 Interprete Calculo Lambda
8/33
Castillo, Martnez, Sainz-Trapaga Pagina 5 de30
de todas ellas. Si bien sintaxis mas complejas (como el caso de los registros con campos nombrados) no pueden definirse conmakeSintaxis, por lo que sera necesario utilizar el metodo anterior, mas extenso pero mas flexible.
Como se dijo anteriormente, al parsear una expresion segun esta sintaxis, se pasan lostokensal constructor para construirel objeto apropiado. Los tokens son todos las instancias de parseElement que no tengan suppressactivado. En el caso delif, los tokens son las tres instancias de Exp. Notar que al constructor del if llegara entonces una lista con tres elementos
que son los resultados de haber parseado recursivamente las 3 expresiones (y por lo tanto son instancias de alguna subclasedeExpresion).
2.4. Tipado
Dado que implementamos un lambda calculo tipado, un elemento fundamental en la definicion de nuevas expresiones sonlas reglas de tipado. El metodo debe devolver una instancia de alguna subclase de Tipo indicando el tipo correspondiente odebe 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 Tipoimplementen un eq adecuado a su semantica.
La funcion tipar recibe ademas como parametro el contexto de tipado, que funciona como diccionario de nombre devariable (string) a su tipo.
2.5. Semantica
La semantica en nuestra implemetancion esta dada por el metodo reducir. Esta reduccion es del tipo big-step, es decirsi un termino tipa, una llamada a reducir debe devolver el valor al que reduce.
A la hora de definir la semantica, puede asumirse como precondicion que el termino tipa, ya que el interprete chequeapreviamente que la expresion tipe antes de intentar reducirla. Es importante asegurarse de que si el termino tipa, la
reduccion no deberia fallar. De esta manera el metodo reducir debe devolver la instancia de expresion que resulta de reducircompletamente al termino.
2.6. Tipos y terminos basicos
En esta seccion presentaremos los tipos basicos vistos en clase junto con su implementacion para nuestro lenguaje.Consideraremos como tipos basicos 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 exactamentede la misma manera en que se definira cualquier extension. Sin embargo, por su naturaleza esencial, son requeridos por casitodas las demas extensiones y por tanto son de interes especial.
2.6.1. Boolean, true y false
Terminos
M ::=true|false
-
7/23/2019 Interprete Calculo Lambda
9/33
Castillo, Martnez, Sainz-Trapaga Pagina 6 de30
Reglas de tipado
true: Bool
f alse: Bool
Semantica
Las expresiones true y false son valores, no reducen.
Implementacion
Esta implementacion, por su sencillez, hace uso de otros dos helpersapropiados para la construccion de tipos basicos(no parametricos) y atomos de algun tipo particular. Se trata de las funciones simpleTypeFactory (que crea una claseapropiada a partir de la sintaxis del termino de tipo) y atomoFactory(que crea una clase hija de Expresion a partir de susintaxis 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 repeticioninnecesaria de codigo en los lugares donde no es necesaria la flexibilidad.
Boolean = simpleTypeFactory(Boolean)Verdadero = atomoFactory(true, Boolean)Falso = atomoFactory(false, Boolean)
2.6.2. Abstracciones
Terminos
M ::=...|X :.M
Reglas de tipado
, X : M :
X :.M :
Semantica
Las abstracciones son valores, no reducen.
-
7/23/2019 Interprete Calculo Lambda
10/33
Castillo, Martnez, Sainz-Trapaga Pagina 7 de30
Implementacion
class Func(Tipo):
def init (self,tok):"""
Define para el tipo funcion el dominio y la imagen.
"""
dom, img = tokself.dominio = domself.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
@classmethoddefsintaxis(cls):
Abs = makeSintaxis((, TipoExp, -> , TipoExp, ) )return Abs
def str (self):s1 =str(self.dominio)s2 =str(self.imagen)ifself.dominio. class == self. class :
return "(%s) -> %s" % (s1, s2)else:
return " %s -> %s" % (s1, s2)
class Abstraccion(Expresion):
def init (self, toks):"""
Una Abstraccion se compone por un id que representa una variable,
un tipo para esa variable y un expresion.
"""
var, tipo, expresion = toksself.var = varself.tipoVar = tipoself.expresion = expresion
defreducir(self):"""
Una Abstraccion reduce a si misma, es un valor.
"""
return self
deftipar(self, namespace):"""
gamma,{X:alpha} | > M : beta----------------------------
gamma | > \X:alpha.M : (alpha -> beta)"""
nam2 = dict(namespace)
nam2[self.var] = self.tipoVarreturn Func([self.tipoVar, self.expresion.tipar(nam2)])
-
7/23/2019 Interprete Calculo Lambda
11/33
Castillo, Martnez, Sainz-Trapaga Pagina 8 de30
defsustituir(self, var, expresion):"""
\X:alpha.M {X \X:alpha.M\X:alpha.M {Y \X:alpha.(M {Y
-
7/23/2019 Interprete Calculo Lambda
12/33
Castillo, Martnez, Sainz-Trapaga Pagina 9 de30
Implementacion
class IfThenElse(Expresion):
def init (self, toks):"""
Define para la estructura IfThenElse la guarda y las
ramas por verdadero y por falso.
"""
guarda, ramaTrue, ramaFalse = toksself.guarda = guardaself.ramaTrue = ramaTrueself.ramaFalse = ramaFalse
defreducir(self):"""
Reduccion de la guarda a un valor. Luego dependiendo de dicho valor
se reduce la rama verdadera o la falsa.
"""ifisinstance(self.guarda.reducir(), Verdadero):
return self.ramaTrue.reducir()else:
return self.ramaFalse.reducir()
deftipar(self, namespace):"""
gamma | > Guarda : Boolean, gamma | > RamaTrue : alpha, gamma | > RamaFalse : alpha---------------------------------------------------------------
gamma | > if Guarda then RamaTrue else RamaFalse : alpha"""
tipoGuarda = self.guarda.tipar(namespace)
tipoRamaTrue = self.ramaTrue.tipar(namespace)tipoRamaFalse = self.ramaFalse.tipar(namespace)# gamma | > Guarda : Booleanif tipoGuarda == Boolean([]):
# gamma | > RamaTrue : alpha, gamma | > RamaFalse : alphaif tipoRamaTrue == tipoRamaFalse:
# gamma | > if Guarda then RamaTrue else RamaFalse : alphareturn tipoRamaTrue
else:raise TypeException(Tipo rama true: %s \n % tipoRamaTrue + \
Tipo rama false:%s \n % tipoRamaFalse + \No existe unificacion entre los tipos de las ramas.)
else:
raise TypeException(Tipo de la guarda: %s \n % tipoGuarda + \El tipo de la guarda no unifica con Boolean.)
defsustituir(self, var, expresion):"""
if M then N else O {X if M {X
-
7/23/2019 Interprete Calculo Lambda
13/33
Castillo, Martnez, Sainz-Trapaga Pagina 10 de30
# if M then N else O
If = makeSintaxis(if , Exp,then, Exp,else, Exp)return If
def str (self):return "if%s then%s else%s" % (self.guarda, self.ramaTrue, self.ramaFalse)
2.6.4. Variables
Terminos
es un conjunto infinito enumerable de variables, X ,
M ::=...|X
Reglas de tipado
{X :}
X :
Semantica
Las variables no reducen, tienen que ser sustitudas.
Implementacion
class Variable(Expresion):
def init (self, tok):"""
Las variables se inicializan con un identificador
compuesto por un string con el primer caracter en mayuscula.
"""self.id = tok[0]
defreducir(self):"""
Las variables reducen a si mismas, tiene que ser sustituidas.
"""
return self
deftipar(self, namespace):"""
{X : alpha} pertenece a gamma----------------------------
gamma | > X : alpha
"""if not (self.id in namespace):
for each in namespace:
-
7/23/2019 Interprete Calculo Lambda
14/33
Castillo, Martnez, Sainz-Trapaga Pagina 11 de30
print each, namespace[each], each. classraise TypeException((No es posible determinar el tipo de la variable %s a + \
partir del contexto: %s.) % (self.id, namespace))return namespace[self.id]
defsustituir(self, var, expresion):
"""X {X TX {Y X s i X ! = Y"""
ifself.id== var:return expresion
else:return self
@classmethoddefsintaxis(cls):
Id = Regex([A-Z][a-zA-Z]*)return Id
def str (self):return self.id
2.6.5. Aplicacion
Terminos
M ::=...|M N
Reglas de tipado
M : , N :
M N :
Semantica
M M
M N M N
N N
V N V N
(X :.M)V M{X V}
-
7/23/2019 Interprete Calculo Lambda
15/33
Castillo, Martnez, Sainz-Trapaga Pagina 12 de30
Implementacion
class Aplicacion(Expresion):
def init (self, toks):"""
Una aplicacion esta compuesta por una funcion aplicadora y
un parametro.
"""
aplicador, parametro = toksself.aplicador = aplicadorself.parametro = parametro
defreducir(self):"""
Se reduce la funcion aplicadora y el parametro, luego:
(\X:alpha.M V) --> M {X M: (alpha -> beta), gamma | > N: alpha------------------------------------------------
gamma | > (M N) : beta"""
x = self.aplicador.tipar(namespace)z = self.parametro.tipar(namespace)
ifisinstance(x, Func):ifx.dominio == z:
return x.imagenelse:
raise TypeException((Tipo del aplicador: %s \n+ \Tipo del parametro: %s \n+ \El tipo del parametro no se corresponde con el domino del tipo del aplicador.
(x'
else:
raise TypeException((Tipo del aplicador: %s \n+ \El tipo del aplicador no se corresponde con el tipo de una Abstraccion.) %x)
defsustituir(self, var, expresion):"""
(M N) {X (M {X
-
7/23/2019 Interprete Calculo Lambda
16/33
Castillo, Martnez, Sainz-Trapaga Pagina 13 de30
def str (self):return "(%s %s)" % (str(self.aplicador), str(self.parametro))
2.6.6. Nat
Implementacion
Nat = simpleTypeFactory(Nat)
2.6.7. Zero
Terminos
M ::=...|zero
Reglas de tipado
zero: N at
Semantica
zero no reduce, es un valor.
Implementacion
Zero = atomoFactory(zero, Nat)
2.6.8. Succ
Terminos
M ::=...|succ(M)
-
7/23/2019 Interprete Calculo Lambda
17/33
Castillo, Martnez, Sainz-Trapaga Pagina 14 de30
Reglas de tipado
M :N at
succ(M) :N at
Semantica
M M
succ(M) succ(M)
Implementacion
class Succ(Expresion):
def init (self, toks):"""
Define para estructura succ la expresion que contiene.
"""
self.termino = toks[0]
defreducir(self):"""
Reducir succ(E) corresponde a reducir E."""
terminoRed = self.termino.reducir()return Succ([terminoRed])
deftipar(self, namespace):"""
gamma | > E : Nat-----------------
gamma | > succ(E) : Nat"""
ifself.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))
defsustituir(self, var, expresion):"""
succ(E) {X succ(E {X
-
7/23/2019 Interprete Calculo Lambda
18/33
Castillo, Martnez, Sainz-Trapaga Pagina 15 de30
return "succ( %s)" % self.termino
2.6.9. Pred
Terminos
M ::=...|pred(M)
Reglas de tipado
M :N at
pred(M) :N at
Semantica
M M
pred(M) pred(M)
pred(succ(M)) M
pred(zero) zero
Implementacion
class Pred(Expresion):
def init (self, toks):"""
Define para estructura succ la expresion que contiene.
"""
self.termino = toks[0]
defreducir(self):"""
Se reduce primero el la expresion que contiene pred, luego:
pred(zero) --> zeropred(succ(v)) --> v
"""
-
7/23/2019 Interprete Calculo Lambda
19/33
Castillo, Martnez, Sainz-Trapaga Pagina 16 de30
termino = self.termino.reducir()ifisinstance(termino, Zero):
return terminoelse:
return termino.termino
deftipar(self, namespace):"""
gamma | > E : Nat-----------------
gamma | > pred(E) : Nat"""
ifself.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))
defsustituir(self, var, expresion):"""
pred(E) {X pred(E {X
-
7/23/2019 Interprete Calculo Lambda
20/33
Castillo, Martnez, Sainz-Trapaga Pagina 17 de30
isZero(succ(M)) false
isZero(zero) true
Implementacion
class IsZero(Expresion):
def init (self, toks):"""
Se define para la estructura isZero la expresion que contiene.
"""self.termino = toks[0]
defreducir(self):"""
Se reduce primero la expresion que contiene isZero, luego:
isZero(zero) --> true
isZero(succ(v)) --> false
"""
termino = self.termino.reducir()ifisinstance(termino, Zero):
return Verdadero([])else:
return Falso([])
deftipar(self, namespace):"""
gamma | > E : Nat----------------
gamma | > isZero(E): Boolean"""
ifself.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))
defsustituir(self, var, expresion):"""
isZero(E) {X isZero(E {X
-
7/23/2019 Interprete Calculo Lambda
21/33
Castillo, Martnez, Sainz-Trapaga Pagina 18 de30
Parte 3
Tutorial
3.1. La extension Maybe
En este tutorial vamos a realizar paso por paso la extension que llamamos Maybe, y que nos permite definir un tipoparametrico Maybe < T >que tiene dos constructores: Nothing < T >y Just J(donde J es un termino de tipo T).
Para construir la extension, tenemos que:
crear el tipo
crear las expresiones basicas Nothing y Just
crear una funcion simple: isNothing
crear una funcion mas compleja: caseMaybe
crear la extension propiamente dicha y registrarla con el interprete
3.2. Codigo boilerplate
Para crear la nueva extension creamos un nuevo archivo de texto vaco en la carpeta extensiones (idealmente conextension .py) y pegamos el siguiente codigo en el:
#!/usr/bin/python
# -*- coding: utf8 -*-
from expresion import Expresion, Tipo, Exp, TipoExp, TypeExceptionfrom expresion import makeSintaxis
from booleans import Booleanfrom lenguaje import Extension
Como veran, se trata de una serie de imports de objetos que vamos a necesitar referenciar en nuestro codigo. Expresiony Tipo son las clases de las que vamos a heredar para construir los terminos y tipos de nuestra extension (que se construyea 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 unaexpresion cualquiera o n termino de tipo cualquiera. TypeException es la excepcion que debemos producir cuando hayaun error de tipos
Finalmente, Boolean es la clase que corresponde al tipo homonimo (y por tanto vive en la extension correspondiente). Es
-
7/23/2019 Interprete Calculo Lambda
22/33
Castillo, Martnez, Sainz-Trapaga Pagina 19 de30
necesaria porque mas adelante vamos a necesitar indicarle al sistema de tipado que un termino es de tipo Boolean (adivinancual?), y para esto es necesario instanciar dicha clase.
3.3. Creando un nuevo tipo
En este caso no se puede utilizar el helper simpleTypeFactory porque este no es un tipo simple: se trata de un tipoparametrico, y por lo tanto hay que definirlo a mano extendiendo directamente la clase Tipo. Comencemos entonces pordefinir la sintaxis:
#######################################
# Declaracion del nuevo tipo Maybe(T) #
#######################################
class TipoMaybe(Tipo):@classmethoddefsintaxis(cls):
return makeSintaxis(Maybe(, TipoExp, ))
Esta declaracion decorada (la lnea que comienza con @ se llama decorator en Python) crea un metodo estatico dela clase TipoMaybe (que luego se llama como TipoMaybe.sintaxis()). En este caso utilizamos el helper makeSintaxis paradecirle al interprete que un termino que describe al tipo TipoMaybe tiene la sintaxis Maybe(TipoExp), donde TipoExprepresenta a una expresion de tipo cualquiera (por ejemplo, Nat, Bool o incluso Maybe(Nat)!).
La definicion sin makeSintaxis hubiera sido mas larga y difcil de leer(y ademas hubiera sido necesario importar Literaldesde pyparsing!):
return Literal(Maybe().suppress() + TipoExp + Literal()).suppress()
Veamos ahora como se construye una instancia de TipoMaybe:
def init (self, toks):self.tipo = toks[0]
La construccion es simple, unicamente se almacena como un atributo de instancia el primer elemento de la lista toks,que corresponde al primer y unico token que matchea la sintaxis del tipo (recordemos que como se utiliza suppress()implcitamente sobre los chunks Maybe( y ), estos no seran recibidos por el constructor). Sigamos:
def eq (self, otro):return self. class == otro. class and \
self.tipo == otro.tipo
Un TipoMaybe < T > sera igual a otro si el segundo es un TipoMaybe (esa es la implementacion heredada de la claseTipo, y no sera necesario sobrecargarla si fuera esta la definicion), pero tambien si el tipo englobado por el Maybe esigual al otro. Recordemos que si bien se utiliza el operador de igualdad, la nocion asociada al mismo es la de unificacion.
def str (self):return Maybe( %s)" % self.tipo
Finalmente, el ultimo metodo define la forma en que se convierte el objeto a una representacion en forma de string
amigable para el usuario. En este caso (y en todos los demas), nos limitamos a reproducir la sintaxis.
Con esto ya tenemos el nuevo tipo definido, y podemos dedicarnos a construir los terminos asociados al mismo.
-
7/23/2019 Interprete Calculo Lambda
23/33
Castillo, Martnez, Sainz-Trapaga Pagina 20 de30
3.4. Definicion de expresiones basicas
A continuacion vamos a definir las dos expresiones basicas del tipo Maybe(T), empezando por Nothing (y esta vezheredando de Expresion en lugar de Tipo).
######################################
# Declaracion de nuevas expresiones #
######################################
class Nothing(Expresion):@classmethoddefsintaxis(cls):
return makeSintaxis(nothing(, TipoExp, ))
La definicion de sintaxis es analoga a la definicion de sintaxis en el termino de tipo. Hay dos particularidades para notaren esta definicion. La primera es que la expresion que definimos como nothing(T) consta de una variable de tipo(denotadapor TipoExp) que permite tipar la expresion. En segundo lugar, no hay que olvidar que los terminos no deben llevar
mayusculas (ya que el parser asume que las cosas que comienzan por mayusculas dentro de una expresion son variables).Por esta razon, el atomo creado se llama nothing(T) y no Nothing(T).
def init (self, toks):self.tipo = toks[0]
def str (self):return nothing( %s) % self.tipo
Tanto el constructor como la funcion de conversion a string son exactamente iguales a las de la definicion del tipo y portanto no requieren explicacion adicional. Veamos ahora que ocurre con las funciones propias de la clase Expresion.
defreducir(self):return self
La funcion reducir() es responsable de producir un termino reducido (con semantica big-step) a partir de la expresionactual. Sin embargo, en el caso de Nothing y por tratarse de un valor, no hay que hacer ninguna reduccion y es suficientecon devolver una referencia al propio objeto que recibe el mensaje.
deftipar(self, namespace):return TipoMaybe([self.tipo])
La funcion tipar() se encarga de producir una instancia del tipo correspondiente a esta expresion. 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 deTypeMaybe < Nat >.
Notese la peculiaridad de que TipoMaybe recibe una lista de tokens, y por lo tanto se debe pasar como parametro[self.tipo] (entre corchetes).
defsustituir(self, var, expresion):return self
Finalmente, el mecanismo de sustitucion, por tratarse de un atomo inmutable, se limita a devolver una referencia alpropio objeto sin sustituir nada (ya que no pueden aparecer variables dentro de esta expresion).
Veamos ahora la definicion de Just J:
-
7/23/2019 Interprete Calculo Lambda
24/33
Castillo, Martnez, Sainz-Trapaga Pagina 21 de30
class Just(Expresion):@classmethoddefsintaxis(cls):
return makeSintaxis(just(, Exp, ))
En este caso la diferencia respecto de la definicion de nothing reside en que la subexpresion es un termino convencionaly no una declaracion de tipo. Esto es p osible puesto que a partir de dicho termino se p odra inferir su tipo y as establecerel tipado de esta expresion. Esto se refleja en la utilizacion de Exp en lugar de TipoExp dentro de la definicion de sintaxis.
def init (self, toks):self.exp = toks[0]
def str (self):return just( %s) % self.exp
defreducir(self):e = self.exp.reducir()return Just([e])
La definicion de reduccion se ajusta a lo definido por el Calculo Lambda: si MM luego Just(M)Just(M). Estoes precisamente lo que se establece aqu, con una llamada recursiva a reducir() de la expresion hija.
deftipar(self, namespace):return TipoMaybe([self.exp.tipar(namespace)])
defsustituir(self, var, expresion):return Just([self.exp.sustituir(var, expresion)])
Estos dos ultimos casos son analogos al anterior: la implementacion no hace mas que llamar recursivamente a las funciones
apropiadas en la subexpresion y construir a partir de dichos resultados los datos que le fueron pedidos.
Con esta ultima clase disp onemos ya de los terminos basicos que constituyen al tipo parametrico MaybeT. Sin embargo,para su uso posterior sera conveniente declarar algunos terminos adicionales que faciliten su utilizacion.
3.5. Definicion de un termino derivado simple
A continuacion nos concierne la declaracion de la expresion isNothing(E) que reduce a true si E es Nothing, o a false siE es Just J. Veamos su implementacion:
-
7/23/2019 Interprete Calculo Lambda
25/33
Castillo, Martnez, Sainz-Trapaga Pagina 22 de30
class IsNothing(Expresion):@classmethoddefsintaxis(cls):
return makeSintaxis(isNothing(, Exp, ))
def init (self, toks):self.exp = toks[0]
def str (self):return isNothing( %s) % self.exp
defreducir(self):e = self.exp.reducir()ifisinstance(e,Nothing):
return Verdadero([])else:
return Falso([])
defsustituir(self, var, expresion):
return IsNothing([self.exp.sustituir(var, expresion)])
Hasta este punto las definiciones son analogas a las vistas anteriormente. Sin embargo, la implementacion del tipado esmas novedosa:
deftipar(self, namespace):st = self.exp.tipar(namespace)ifisinstance(st, TipoMaybe):
return Boolean([])else:
raise TypeException(Tipo del Termino: %s \n +El tipo del termino debe ser Maybe(T). % st)
Observando la logica del codigo, vemos que lo que hace es tipar el subtermino (es decir, si estamos lidiando con isNot-hing(E), obtiene el tipo de E) y a continuacion se fija si dicho tipo es realmente Maybe, verificando si es una instancia deTipoMaybe. En caso afirmativo, el tipo de la expresion isNothing sera 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 esperadoy cual el que se recibio.
3.6. Definicion de un termino derivado complejo
Como ultima declaracion analizaremos la implementacion de CaseMaybe, el observador en base a constructores de las
instancias deMaybe < T >.
class CaseMaybe(Expresion):@classmethoddefsintaxis(cls):
return makeSintaxis(caseMaybe(, TipoExp, ), Exp, ; of nothing -> , Exp, ; just(J) -> , Exp)
def init (self, toks):self.tipo, self.exp, self.resNothing, self.resJust = toks
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 utilizo el nombre
-
7/23/2019 Interprete Calculo Lambda
26/33
Castillo, Martnez, Sainz-Trapaga Pagina 23 de30
fijo Jpara la variable del case.
defreducir(self):expR = self.exp.reducir()ifisinstance(expR, Nothing):
return self.resNothing.reducir()
else:return self.resJust.sustituir(J, expR.exp).reducir()
La primera diferencia que se observa entre esta extension y la anterior se da en el caso de la reduccion: en este caso, esnecesario para reducir exitosamente ligar la variable J a la expresion asociada. Esto es precisamente lo que hace el codigosi detecta que la expresion interna (self.exp) es una instancia de Just.
deftipar(self, namespace):maybet = self.exp.tipar(namespace)trn = self.resNothing.tipar(namespace)
ns nuevo = namespace.copy()
ns nuevo[J] = self.tipotrj = self.resJust.tipar(ns nuevo)
ifisinstance(maybet, TipoMaybe)and trn == trj:return trn
else:raise TypeException((Tipo del Termino: %s \n +
Tipo del bloque Case Nothing: %s \n +Tipo del bloque Case Just: %s \n +El tipo de ambos casos debe ser compatible, y el tipo del termino deba
ser Maybe(T).)'
(maybet, trn, trj))
La implementacion de la regla de tipado es similar a la que vimos en isNothing, aunque el chequeo a realizar es m ascomplejo (ya que deben asegurarse mas condiciones). En este caso, se verifica que ambas ramasdel case tengan el mismotipo despues de ligar la variable J en el caso de Just, as como que self.exp tenga el tip o apropiado segun la declaracion delcase.
Una peculiaridad es el uso del namespace: notese que a diferencia de los casos anteriores, el contenido del mismo semodifica. El namespace es un diccionario nativo de Python, que mapea strings a instancias de subclases de Tipo. Dadoque los diccionarios (al igual que practicamente todo en Python) se pasan por referencia, es necesario copiar el namespaceexplcitamente antes de realizar la modificacion del mismo ya que de otro modo se corre el riesgo de contaminar el namespacede otras expresiones que compartan la misma instancia del diccionario.
defsustituir(self, var, expresion):sMaybe = self.exp.sustituir(var, expresion)sresNothing = self.resNothing.sustituir(var, expresion)
ifvar != J:sresJust = self.resJust.sustituir(var, expresion)
else:sresJust = self.resJust
return CaseMaybe([self.tipo, sMaybe, sresNothing, sresJust])
Por ultimo, el mecanismo de sustitucion tambien es mas complejo: en este caso, y como hay una variable ligada por estaexpresion, debe asegurarse de que no se realice una sustitucion de la misma por pedido de una expresion de nivel mas alto.
En este punto se establece el scope de la variable J, y se determina que su valor dentro del bloque correspondiente
-
7/23/2019 Interprete Calculo Lambda
27/33
Castillo, Martnez, Sainz-Trapaga Pagina 24 de30
del case sera establecido por self.reducir() y no por una sustitucion anterior. As, frente a un pedido de sustitucion, sereemplazara cualquier variable menos J, que es local puesto que se define a nivel del Case.
3.7. Creacion de la extension
Finalmente, el unico punto pendiente es la creacion de la extension propiamente dicha. El codigo es autoexplicativo: nohay mas que instanciar la clase Extension con una etiqueta apropiada.
######################################
# Armado de la nueva extension #
######################################
extensionMaybe = Extension(Maybe, expresiones=[Just, Nothing, IsNothing, CaseMaybe], tipos=[TipoMaybe])
Por ultimo, para que la extension sea detectada por los aplicativos (tanto de consola como la interfaz grafica, es necesarioagregarla a la lista extensionesen registro.py. Esta lista es consultada por las aplicaciones para conocer las extensiones
que estan disponibles.
-
7/23/2019 Interprete Calculo Lambda
28/33
Castillo, Martnez, Sainz-Trapaga Pagina 25 de30
Parte 4
Extensiones
Los siguientes son los tipos y expresiones definidos p or nosotros a modo de librera.
4.1. Tipos
Boolean
Natural
Funcion ( )
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
Aplicacion (M N)
Registros:
construccion de registros
proyeccion de registros
Listas:
Lista vacaagregar elemento adelante (add(E,L))
case para listas (esquema para pattern matching)
-
7/23/2019 Interprete Calculo Lambda
29/33
Castillo, Martnez, Sainz-Trapaga Pagina 26 de30
Arboles binarios:
nil
constructor de arbol binario (bin(M,N,O))
case para arboles
Maybe
NothingJust
isNothing
case para Maybe
Tuplas
constructor de tupla
proyectores
-
7/23/2019 Interprete Calculo Lambda
30/33
Castillo, Martnez, Sainz-Trapaga Pagina 27 de30
Parte 5
Posibles extensiones al trabajo
5.1. Subtipado
Una ampliacion posible para el traba jo sera la incorporacion de reglas de subtipado. Esto es factible dentro del esquemapropuesto, y la forma simple de hacerlo sera sobrecargar el operador< para los tipos. De este modo, en vez de preguntarsi un tipo es igual a otro, se pregunta por menor o igual al momento de realizar los chequeos de tipos.
El siguiente codigo implementa la extension de registros:
class TypeReg(Type):
"""
El tipo registro estara compuestos por etiquetas con tipos asociados.
"""def init (self, tok ):
i = 0ids = {}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):ifisinstance(otro, TypeReg):res =Truefor each in self.ids:
res = res and each in otro.ids and self.ids[each] == otro.ids[each]if(not res):
return res
returnlen(self.ids) ==len(otro.ids)else:
return False
@classmethoddefsintaxis(cls):
campo = makeSintaxis(Id, :, TypeExp)listaCampos = Optional(campo + ZeroOrMore(makeSintaxis(",", campo)))
-
7/23/2019 Interprete Calculo Lambda
31/33
Castillo, Martnez, Sainz-Trapaga Pagina 28 de30
reg = makeSintaxis({, listaCampos,})return reg
def str (self):s = {primero =True
for each in self.ids:ifprimero:
primero =Falses+= 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:
{Li|1 i n} {kj|1 j m}, kj =li Sj
-
7/23/2019 Interprete Calculo Lambda
32/33
Castillo, Martnez, Sainz-Trapaga Pagina 29 de30
5.2. Inferencia de tipos y otras alternativas
Durante la cursada vimos como implementar el algoritmo de inferencia de tipos para el calculo lambda (que ademasimplemetamos en una clase de laboratior). Por esta razon, decidimos usar tipado simple y no inferencia de tipos paraenfocarnos en el aspecto central de nuestro trabajo que era la simplicidad de extension.
Del mismo modo, podra construirse como extension futura el agregado de polimorfismo, as como tambien junto coneste considerar el desarrollo de clases de tipos (similares a las de Haskell, donde tenemos Eq para denotar que los tipos tienencomparacion por igual, o Show para denotar a los tipos que tienen una funcion show para ser mostrados por pantalla).
-
7/23/2019 Interprete Calculo Lambda
33/33
Castillo, Martnez, Sainz-Trapaga Pagina 30 de30
Parte 6
Conclusion
Como conclusion final po demos enumerar una serie de conceptos que resultaron de la realizacion de este trabajo practico.
En primer lugar, y como se explico en la introduccion, la idea de este trabajo era po der lograr un interprete de lambdacalculo que no solo funcionara de manera correcta, sino que tambien pudiese ser extendido para incluir nuevos tipos yexpresiones de forma sencilla.
Consideramos que este objetivo se cumplio, ya que pudimos elaborar una manera estandar de introducir nuevas extensio-nes al interprete. Ademas, consideramos que hallamos un equilibrio razonable entre la sencillez de extension y la flexibilidadque se permite al agregar nuevos terminos al lengua je. No se utilizaron conveciones extremadamente fuertes que podranhaber causado limitaciones po co practicas para extender el lenguaje de forma extravagante. A su vez, la inclusion del tutorialdebera hacer que la incorporacion de una nueva extension sea un procedimiento sencillo incluso para aquellas personas queno estan familiarizadas con el trabajo ni con el lenguaje Python.
Por otro lado creemos que el enfoque del trabajo se oriento a uno de los temas mas importantes de la cursada, y que selogro no solo combinarlo con programacion orientada a objetos, sino tambien encontrar una forma dinamica de resolverlo. Si
bien es cierto que durante la cursada se implementaron cuestiones relacionadas al lambda caculo, la vision de este traba jo esmas compacta y abarca las cuestiones principales del tema (como ser el manejo de tipado, sintaxis, reducci on y extensionesal calculo). Ademas, los trabajos practicos llevados a cabo durante la cursada tuvieron enfoques menos globales que este,que nos permitio construir un interprete de un lengua je de programacion de principio a fin.
Como senalamos en el apartado anterior, nos hubiese gustado tambien implementar otras cuestiones como es el caso delas reglas de subtipado. Sin embargo, se dejaron las puertas abiertas a dicha extension como se comento anteriormente.
Consideramos que como punto final a la materia, la realizacion de un trabajo practico constituyo una solucion dinamica einteractiva, y nos permitio utilizar lo aprendido para construir una herramienta completa, que podra tener posibilidades deuso futuro dentro de la materia. Futuros estudiantes pueden examinar el codigo entregado para comprender el funcionamientode un interprete, y servirse de la gran cantidad de extensiones propuestas como referencia para implementar las suyas.