0% encontró este documento útil (0 votos)
278 vistas43 páginas

Haskell

El documento presenta una introducción a Haskell, un lenguaje de programación funcional. Explica que Haskell se basa en el cálculo lambda y el paradigma de programación funcional. Brevemente describe el contexto histórico del lenguaje y sus características clave como la evaluación perezosa y la pureza funcional. Luego, pasa a repasar elementos técnicos de Haskell como su sistema de tipos, funciones, tipos de datos y clases de tipos, dando ejemplos. Finalmente, concluye y presenta una bibliografía.

Cargado por

Gaby Gaviothitha
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
278 vistas43 páginas

Haskell

El documento presenta una introducción a Haskell, un lenguaje de programación funcional. Explica que Haskell se basa en el cálculo lambda y el paradigma de programación funcional. Brevemente describe el contexto histórico del lenguaje y sus características clave como la evaluación perezosa y la pureza funcional. Luego, pasa a repasar elementos técnicos de Haskell como su sistema de tipos, funciones, tipos de datos y clases de tipos, dando ejemplos. Finalmente, concluye y presenta una bibliografía.

Cargado por

Gaby Gaviothitha
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

75.

31 Teora de Lenguajes

Haskell

Albani Francisco 84891

[Link] cuatrimestre 2006


Resumen
El presente informe comienza presentando al lector algunos concep-
tos previos necesarios para adentrarse en el mundo de Haskell. Se dan
breves ideas sobre la importancia del Paradigma de Programacion Fun-
cional y sobre el significado del Calculo Lambda. Luego, aportandole un
toque de color al informe, se recorre el contexto historico del nacimiento
de Haskell. Finalizada la introduccion, se presentan las caractersticas mas
importantes del lenguaje de una forma principalmente teorica, para luego
dejarle lugar a un enfoque mas tecnico, donde se repasan varios de los ele-
mentos que constituyen a Haskell, aportando ejemplos all donde vinieran
a ilustrar y dar soporte a las ideas. Es quiza necesario advertir al lector
que en ningun momento el presente informe intenta servir de tutorial ni
profundizar demasiado en todos los detalles.

1
Indice

I Introduccion 4
1. Que es Haskell ? 4

2. Paradigma de Programacion Funcional 5


2.1. Contexto historico . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2. Caractersticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3. Modularidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

3. Calculo Lambda 8

4. Un poco de Historia 9
4.1. El nacimiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.2. El Bautismo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

II Caractersticas Especiales 12
5. Evaluacion perezosa 12

6. Pureza funcional 13

7. Programacion de Alto Orden 14

8. Monadas 15

III Caractersticas Tecnicas 17


9. Sistema de Tipos 17
9.1. Sinonimia de Tipos . . . . . . . . . . . . . . . . . . . . . . . . . . 17

[Link] 18
10.1. Pattern Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
10.1.1. As-patterns . . . . . . . . . . . . . . . . . . . . . . . . . . 19
10.1.2. Wild-cards . . . . . . . . . . . . . . . . . . . . . . . . . . 19
10.1.3. Construcciones case of . . . . . . . . . . . . . . . . . . . . 19
10.2. Variables locales . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
10.3. Guardas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
10.4. Tipo de las funciones . . . . . . . . . . . . . . . . . . . . . . . . . 21
10.5. Funciones polimorficas . . . . . . . . . . . . . . . . . . . . . . . . 21
10.6. Currying . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
10.7. Expresiones Lambda . . . . . . . . . . . . . . . . . . . . . . . . . 22

2
[Link] 23

[Link] de Tipos 24

[Link] de datos algebraicos 25


13.1. Campos etiquetados . . . . . . . . . . . . . . . . . . . . . . . . . 25
13.2. Enumerados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
13.3. Recursivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
13.4. Polimorficos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

[Link] de tipos 30
14.1. Polimorfismo Ad-Hoc . . . . . . . . . . . . . . . . . . . . . . . . . 30
14.2. Extension de clases . . . . . . . . . . . . . . . . . . . . . . . . . . 31
14.3. Instancias derivadas . . . . . . . . . . . . . . . . . . . . . . . . . 32
14.4. Comparacion con otros lenguajes . . . . . . . . . . . . . . . . . . 32

[Link] monadas como una clase de tipo 34


15.1. Las monadas mas ilustres . . . . . . . . . . . . . . . . . . . . . . 34
15.1.1. Maybe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
15.1.2. Las listas tambien son monadas . . . . . . . . . . . . . . . 35
[Link]. Comprension de Listas . . . . . . . . . . . . . . 35
15.1.3. IO Monad . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
[Link]. Do-notation . . . . . . . . . . . . . . . . . . . . 38

[Link] 39
16.1. Restriccion al importar . . . . . . . . . . . . . . . . . . . . . . . . 40
16.2. Restriccion al exportar (ADTs) . . . . . . . . . . . . . . . . . . . 40

IV Conclusion 41

V Bibliografa 42

3
Haskell 4

Parte I
Introduccion
1. Que es Haskell ?
Haskell is probably doomed to succeed.
Tony Hoare1
Lisp was the most beautiful language in the world
At least up until Haskell came along.
Larry Wall2

Haskell es un lenguaje de programacion de amplio espectro inspirado fuerte-


mente en el Calculo Lambda, que responde al Paradigma de Programacion Fun-
cional. Su semantica no estricta, su pureza funcional y el manejo de tipos que
posee, lo destacan especialmente del resto de los lenguajes.
Su diseno es llevado a cabo por un comite formado en 1987 para el cual uno
de los objetivos mas importantes siempre ha sido la elegancia matematica.

Programar grandes sistemas de software es caro y dificultoso. El manten-


imiento de esos sistemas lo es aun mas. Los lenguajes de programacion fun-
cionales, como Haskell, pueden hacerlo mas facil y barato. Es comun escuchar
decir, entre quienes han probado Haskell, que el sistema de tipos es muy bueno a
la hora de evitar varias clases errores bastante comunes en la practica. Inclusive
entre quienes no pueden utilizar Haskell en sus proyectos, el haberlo aprendido
les a ayudado a pensar los problemas desde otra perspectiva, y construir mejores
soluciones en otros lenguajes.

Segun sus creadores, Haskell ofrece:

Incremento en la productividad del programador.


Codigo mas corto, claro y facil de mantener.
Menor cantidad de errores y mayor confiabilidad.
Una distancia semantica menor entre el programador y el lenguaje.
Eliminacion de una altsima cantidad de interacciones imprevistas, pro-
ducto del riguroso control de efectos colaterales.

1 Cientfico britanico famoso por, entre otras cosas, la invencion del Quicksort, el algoritmo

de ordenamiento mas utilizado en el mundo.


2 Programador californiano famoso por ser el autor del lenguaje de programacion PERL y

por haber recibido el primer premio para el Avance del Software Libre de la Fundacion para
el Software Libre.

75.31 Teora de Lenguajes 4 [Link] cuatrimestre 2006


Haskell 5

2. Paradigma de Programacion Funcional


Haskell, Lisp, and SQL are the only programming
languages that Ive seen where one spends
more time thinking than typing.
Philip Greenspun3

2.1. Contexto historico


En la decada de 1930, Alonzo Church y Stephen Kleene inventan el Calculo
Lambda, un marco teorico que describe las funciones y su evaluacion. Aunque es
una abstraccion matematica y no un lenguaje de programacion, ha formado la
base de casi todos los lenguajes funcionales. La Logica Combinacional, creada
por Moses Schonfinkel y luego desarrollada por Haskell Curry con objetivos mas
generales, precedio al Calculo Lambda en su invencion pero comenzo a tomar
importancia a partir de la decada de 1960 cuando la Ciencia teorica de la Com-
putacion comenzo a interesarse por ella.

En 1978, John Backus4 , al recibir el Premio Turing 5 , titulo su conferencia:


Puede la programacion ser liberada del estilo de von Neumann6 ? Un estilo
Funcional y su algebra de programas[Bac78], posicionando a la programacion
funcional como un ataque radical hacia todo lo establecido en el mundo de la
programacion. Semejante proposicion pronunciada por un gigante en el campo,
situo en el mapa a la programacion funcional de una nueva manera: como una
herramienta practica en vez de una mera curiosidad matematica.

En 1984, John Hughes, publico un artculo titulado Por que la progra-


macion funcional importa? [Hug89], donde pretenda demostrarle al mundo re-
alque la programacion funcional era de vital importancia, y tambien ayudar a
los programadores funcionales a explotar sus ventajas al maximo, especificando
claramente cuales eran esas ventajas.
He aqu la introduccion de aquel artculo:

A medida que el software se vuelve mas y mas complejo, es mas y


mas importante estructurarlo bien. El software bien estructurado es
facil de escribir, facil de depurar, y provee una coleccion de modulos
que pueden ser reutilizados para reducir futuros costos de progra-
macion. Los lenguajes convencionales imponen lmites conceptuales
3 Profesor de Ingeniera de Software para aplicaciones de Internet e Ingeniera Electrica

en el Instituto de Tecnologa de Massachusetts (M.I.T.).


4 Backus lidero el equipo que desarrollo FORTRAN, entre otras cosas muy trascendentes.
5 El Premio Turing es considerado por muchos como el Premio Nobel de la Computacion.

Es otorgado anualmente por la Association for Computing Machinery a quienes hayan con-
tribuido de manera trascendental al campo de las ciencias computacionales.
6 La mayora de los lenguajes de programacion entran en esta categora. Ejemplos son

FORTRAN, C y Java.

75.31 Teora de Lenguajes 5 [Link] cuatrimestre 2006


Haskell 6

en la forma en la que los problemas pueden modularse. Los lenguajes


funcionales empujan esos lmites hacia atras. [...]
Siendo la modularidad la clave para una programacion exitosa, los
lenguajes funcionales son de vital importancia para el mundo real.

2.2. Caractersticas
La programacion funcional recibe ese nombre porque un programa se con-
struye solamente a partir de funciones. El propio programa principal es escrito
como una funcion que recibe una entrada como argumento y entrega un resul-
tado como salida. Tpicamente, la funcion principal es definida en terminos de
otras funciones, que a su vez son definidas en los terminos de otras funciones,
hasta llegar al ultimo nivel, donde las funciones son primitivas del lenguaje. To-
das estas son funciones en el mas estricto sentido matematico; lo que inclusive
se ve reflejado en el momento de su definicion, que se lleva a cabo por medio de
ecuaciones.
La programacion funcional esta contenida en un paradigma mas general lla-
mado Programacion Declarativa, cuya principal caracterstica es la de describir
los objetivos de un algoritmo sin especificar su implementacion7 . De ah es de
donde los programas funcionales heredan la cualidad de no contener declara-
ciones de asignacion; una variable nunca cambia su valor inicial. Mas general-
mente, los programas funcionales no contienen ningun tipo de efecto colateral.
Una llamada a una funcion no puede tener otro efecto mas que el computo de
su resultado. Esto elimina una de las principales fuentes de errores, y tambien
hace que el orden de ejecucion sea irrelevante, ya que si ningun efecto colateral
puede alterar el valor de una expresion, entonces puede ser evaluada en cualquier
momento. Esto releva al programador de la carga de tener que controlar el flujo.
Debido a que las expresiones pueden ser evaluadas en cualquier momento, las
variables pueden reemplazarse libremente por sus valores y viceversa, sin alterar
el resultado del programa. Esta caracterstica es conocida como Transparencia
Referencial, y hace a los programas funcionales matematicamente mas tratables.

2.3. Modularidad
El diseno modular trae consigo una gran mejora en la productividad. Primero,
pequenos modulos pueden ser escritos facil y rapidamente. Segundo, los modulos
de proposito general pueden ser reutilizados, llevando a un desarrollo mas rapido
de aplicaciones subsecuentes. Tercero, los modulos de un programa pueden ser
probados independientemente, ayudando a reducir el tiempo de depuracion.
Sin embargo, hay algo importante que usualmente es olvidado. Cuando se
escribe un programa modular para resolver un problema, primero se divide al
problema en partes, se las resuelve y luego se combinan las soluciones. Las
formas en las que el problema original puede ser dividido depende directamente
7 No intenta ser una definicion rigurosa; cada lenguaje refleja esta caracterstica en mayor

o menor medida.

75.31 Teora de Lenguajes 6 [Link] cuatrimestre 2006


Haskell 7

de las formas en las que las soluciones pueden ser combinadas. Por lo tanto,
para incrementar la capacidad de modular un problema, un lenguaje tiene que
proveer nuevas formas de combinar soluciones.
La programacion funcional provee dos nuevas formas de combinar soluciones:
las funciones de alto orden, y la semantica no estricta. La clave del poder de la
programacion funcional es permitir mejoras significantes en la modularidad.

75.31 Teora de Lenguajes 7 [Link] cuatrimestre 2006


Haskell 8

3. Calculo Lambda
El Calculo Lambda es un sistema formal de notacion para investigar y de-
scribir la definicion y aplicacion de funciones matematicas e inclusive de algo-
ritmos. Fue disenado por Alonzo Church y Stephen Kleene en la decada del 30.
Church lo utilizo para, en 1936, dar una respuesta negativa al famoso proble-
ma de decision conocido como Entscheidungsproblem 8 . Cualquier funcion com-
putable puede ser expresada y evaluada con este formalismo. Es, por lo tanto,
equivalente a una Maquina de Turing.
Lo que matematicamente se expresara f (x) = x2 , en el Calculo Lambda se
expresara x.x2 . La especializacion de f (x) en un valor a arbitrario, f (a), se
expresa (x.x2 ) a; resultando en la equivalencia entre a2 y x.a2 .
Es importante destacar que en el Calculo Lambda (y en Haskell) no hay una
distincion entre valores y funciones, lo que permite utilizar estas ultimas como
argumentos de otras funciones. Este concepto se ampliara en la Seccion 7.

El conjunto de todas las expresiones lambda puede ser descripto por la sigu-
iente gramatica libre de contexto:

e ::= x | x.e | eo e1
Donde:
x es un identificador que representa a una variable en el sentido matematico.

e, e0 y e1 son expresiones lambda.

8 Consiste en averiguar si existe un algoritmo que decida si una declaracion logica de primer

orden, es universalmente valida o no.

75.31 Teora de Lenguajes 8 [Link] cuatrimestre 2006


Haskell 9

4. Un poco de Historia
4.1. El nacimiento
En Septiembre de 1987, una reunion impulsada por Simon Peyton Jones,
Paul Hudak y Philip Wadler, se llevo a cabo en la Conferencia de Lengua-
jes de Programacion Funcional y Arquitecturas de Computadores (FPCA)9 en
Portland, Oregon, con el objetivo de discutir la desafortunada situacion que
atravesaba la comunidad de lenguajes de programacion funcional: exista mas
de una docena de lenguajes puros y no estrictos, todos muy similares en poder
expresivo y semantico, y el uso mas extenso de esta clase de lenguajes estaba
siendo obstaculizado por la carencia de un lenguaje comun. Se decidio que un
comite deba formarse para disenar tal lenguaje, permitiendo una mejor comu-
nicacion de ideas, una base estable para el desarrollo de aplicaciones reales, y
un vehculo a traves del cual otros pudieran sentirse motivados a usar lenguajes
funcionales.
En un principio, la idea era tomar como base un lenguaje ya existente,
para luego evolucionar en la direccion mas conveniente. De todos los lenguajes
no estrictos en desarrollo, el mas maduro, por lejos, era Miranda: un lenguaje
puro, bien disenado, con muchas cosas en comun a las ideas del comite y una
implementacion robusta producida por la empresa de David Turner, Research
Software Ltd.
Luego de una breve y cordial charla, Turner no acepto que Miranda sea
adoptado como punto de partida para la creacion de este nuevo lenguaje. Sus
objetivos eran distintos a los del comite: estaba fuertemente determinado a man-
tener un solo standard y no quera que existieran multiples dialectos de Miranda
en circulacion. Inclusive pidio que el nuevo lenguaje fuera lo suficientemente dis-
tinto de tal forma que no fueran confundidos. Tampoco acepto la invitacion a
formar parte del comite.
Este impedimento, que obligo a comenzar el desarrollo desde cero, permi-
tio contemplar enfoques mas radicales para muchos aspectos del diseno. Muchas
caractersticas actuales de Haskell hubieran sido improbables de haber sido de-
sarrollado a partir de Miranda. Sin embargo, Haskell tiene una considerable
deuda con Miranda, tanto por la inspiracion general como por algunos elemen-
tos especficos adoptados libremente para satisfacer algunas caractersticas del
diseno emergente.

La primer reunion fsica llevada a cabo luego de la FPCA, fue en Enero del
88, en la Universidad de Yale, ubicada en New Heaven, Connecticut. Lo primero
fue establecer los siguientes objetivos para el lenguaje:
Deba ser adecuado para la ensenanza, la investigacion y el desarrollo de
grandes aplicaciones.
Deba ser completamente descripto a traves de la publicacion de una sin-
taxis formal y una semantica.
9 Functional Programming and Computer Architecture Conference.

75.31 Teora de Lenguajes 9 [Link] cuatrimestre 2006


Haskell 10

Deba ser disponible libremente. Deba permitirse que cualquier persona


pudiera implementar el lenguaje y distribuirlo a quien quisiera.
Deba poder ser usado como base para la investigacion de lenguajes.
Deba estar basado en ideas que gozaran de un consenso considerable.
Deba reducir la innecesaria diversidad entre los lenguajes funcionales.
Los ultimos dos objetivos reflejaban la intencion de que el lenguaje fuera
un tanto conservador, en lugar de pretender grandes innovaciones; si bien luego
todo resulto bastante diferente.

75.31 Teora de Lenguajes 10 [Link] cuatrimestre 2006


Haskell 11

4.2. El Bautismo
La eleccion del nombre se llevo a cabo de la siguiente manera: cada miembro
del comite poda proponer todos los que quisiera, para luego ser escritos en un
pizarron. Al final de este proceso, los siguientes nombres aparecieron:
Semla
Haskell
Vivaldi
Mozart
CFL (Common Functional Language)
Funl 88
Semlor
Candle (Common Applicative Notation for Denoting Lambda Expressions)
Fun
David
Nice
Light
ML Nouveau (o Miranda Nouveau, o LML Nouveau, o ...)
Mirabelle
Concord
LL
Slim
Meet
Leval
Curry
Frege
Peano
Ease
Portland
Haskell B Curry

Luego de considerables discusiones a cerca de cada uno de los nombres, cada


persona fue libre de tachar uno que no le gustara. Cuando esto fue terminado,
solamente quedo uno.
Ese nombre fue Curry, en honor al logico matematico Haskell Brooks Curry.
Pero al notar que el nombre se prestaba para varios juegos de palabras burlescos,
y estaba relacionado con el actor Tim Curry, muy famoso en aquel momento
por su papel de cientfico loco en The Rocky Horror Picture Show, se opto por
establecer Haskell como el nombre definitivo.
Tiempo despues, Hudak visito a la viuda de Haskell Curry, para preguntarle
si tena problemas en que se use el nombre de su marido, a lo que respondio neg-
ativamente. Luego de eso, la Senora Curry asistio a una charla de Hudak, sobre
Haskell (el lenguaje); y mas alla de no haber entendido una sola palabra de lo
que se hablo, se mostro muy agradecida. Lo mas memorable de lo que dijo fue:
Saben?, a Haskell nunca le gusto el nombre Haskell.

75.31 Teora de Lenguajes 11 [Link] cuatrimestre 2006


Haskell 12

Parte II
Caractersticas Especiales
5. Evaluacion perezosa
Efficiency is intelligent laziness.
David Dunham

Dentro del conjunto de las Estrategias de Evaluacion, se encuentran prin-


cipalmente dos grandes subconjuntos: las estrategias de Evaluacion Estricta y
las estrategias de Evaluacion no-Estricta. En el primer conjunto, encontramos
a las estrategias donde las expresiones son evaluadas en el momento en el que
se las encuentra10 . En el segundo conjunto, tenemos a las estrategias donde la
evaluacion de una expresion se difiere hasta un momento posterior. Un ejemp-
lo de estas es call-by-name 11 , que consiste en reemplazar en el momento en el
que se necesite, en el cuerpo del contexto, las demandas a la expresion por la
expresion directamente. Una version mejorada de call-by-name es call-by-need,
mas conocida como evaluacion perezosa o laziness, que solo evalua una sola vez
la expresion para evitar repetir los calculos.
En Haskell, de forma predeterminada y con algunas excepciones, las expre-
siones se evaluan por call-by-need. El lenguaje provee los mecanismos necesarios
para especificar la estrategia segun sea necesario.
La Evaluacion perezosa tiene sus costos. Usualmente es menos eficiente que
las estrictas, debido a la contabilidad adicional requerida para satisfacer todas
sus caractersticas. El costo es significante pero al haberse considerado un factor
constante en la mayora de los casos, esta opcion prevalecio por encima del resto.
Sucede tambien que es muy difcil, inclusive para los programadores expertos,
predecir el comportamiento espacial de un programa perezoso especialmente
cuando este factor no es constante, lo que condujo a replantear el diseno de tal
forma de permitir ciertos comportamientos estrictos.
Por otro lado, varios lenguajes estrictos han comenzado a incluir caractersti-
cas perezosas al reconocer las virtudes que tiene en muchos casos. Como un
resultado de esto, la division entre lo estricto y lo perezoso ha dejado de verse
como una cuestion de blancos y negros.

10 Estas son las elegidas por la mayora de los lenguajes. Vale la pena aclarar que aun en

estos, en general, las estructuras if-then-else solo evaluan la expresion que corresponde al
camino que el flujo a de tomar.
11 Esta estrategia es mas un concepto teorico que un recurso practico.

75.31 Teora de Lenguajes 12 [Link] cuatrimestre 2006


Haskell 13

6. Pureza funcional
Once we were committed to a lazy language, a pure one was inescapable.
A history of Haskell [His07].

Una consecuencia inmediata de la evaluacion perezosa es que el orden en el


que son evaluadas las expresiones, no esta dado por el orden de sus apariciones,
como se esperara en un lenguaje estructurado, si no que esta dado por el orden
en el que van siendo requeridas. Esto imposibilita realizar de forma confiable
operaciones de entrada/salida o que impliquen algun otro tipo de efecto colat-
eral a la llamada de alguna funcion. Una vez elegida la evaluacion perezosa, el
comite no pudo escapar a construir Haskell en base a funciones puras. Esto es,
en otras palabras, limitarlas a tomar parametros y devolver resultados sin poder
provocar ningun tipo de efecto colateral a su ejecucion en el mas estricto sentido
matematico.
Otra caracterstica importante que hace a Haskell un lenguaje puro, es ex-
cluir la posibilidad de realizar cambios destructivos sobre los datos. Esta es
quiza una de las diferencias mas radicales con respecto a la programacion es-
tructurada, donde cambiar el valor de una variable es uno de los recursos mas
comunes a la hora de escribir algoritmos. Una consecuencia de esto es que las
variables refieren siempre a valores inmutables y persistentes. Se conoce como
Transparencia referencial a la propiedad de un lenguaje en el cual todas sus
expresiones pueden ser reemplazadas con su resultado sin cambiar el compor-
tamiento del programa resultante. Esto esta en estrecha relacion con la pureza
de las funciones, pues ante mismos parametros, devuelven siempre el mismo re-
sultado, permitiendo implementar una tecnica de optimizacion conocida como
Memoization, que consiste en recordar los resultados de las llamadas anteriores
para no tener que volver a computarlos. A los lenguajes donde, por el contrario,
no es posible realizar el reemplazo de sus expresiones por sus resultados sin al-
terar el resultado, se les atribuye la propiedad de Opacidad referencial.

La cara oscura de esta caracterstica es que estaramos limitados a progra-


mar sistemas que respondan a un modelo muy simple de entrada, procesamiento
y salida de datos, donde la unica interfaz con el exterior seran entrada y sal-
ida. En un principio, el sistema de entrada y salida de Haskell era muy torpe
y considerado una fuente de verguenza para sus disenadores. Pero finalmente
resulto ser la madre de una invencion que es considerada como la mayor con-
tribucion de Haskell al mundo de los lenguajes de programacion: entrada/salida
monadica 12 .

12 La seccion 8 esta dedicada a este concepto.

75.31 Teora de Lenguajes 13 [Link] cuatrimestre 2006


Haskell 14

7. Programacion de Alto Orden


Haskell hereda el rasgo del Calculo Lambda de no diferenciar sustancial-
mente a las funciones de los valores. Se suele decir que las funciones, son valores
de primer orden; esto significa que estan al mismo nivel que los enteros, los
caracteres y cualquier otro tipo de dato. Pueden ser ligadas a variables, alma-
cenadas en colecciones, ser pasadas como parametro de otras funciones y ser
devueltas como valor de retorno de una funcion. Los ejemplos mas comunes en
los lenguajes funcionales de funciones de Alto Orden son map, filter y foldl
o foldr.
Las funciones de Alto Orden pueden ser vistas como una forma de ejecucion
diferida, donde una funcion es definida en un contexto y pasada a otro diferente
para luego ser invocada. La diferencia con las funciones comunes encontradas
en otros lenguajes, es que las funciones de Alto Orden representan abstracciones
lambda anonimas, permitiendo que el contexto invocador no necesite conocer el
nombre del la funcion.
En otros lenguajes existen las llamadas Clausuras lexicograficas, que comple-
mentan la idea anterior anexando a la funcion todo el entorno de su definicion,
permitiendole acceder a este inclusive cuando es invocada desde un contexto
distinto.

75.31 Teora de Lenguajes 14 [Link] cuatrimestre 2006


Haskell 15

8. Monadas
Haskell is the worlds finest imperative programming language
Simon Peyton Jones.
[Pey00]

Las caractersticas anteriores hacen a Haskell un lenguaje muy elegante.


Pero todas ellas eluden hablar de cosas como operaciones de Entrada/Salida,
deteccion y recuperacion de errores, concurrencia e interaccion con libreras o
componentes escritos en otros lenguajes. Todas ellas un tanto traumaticas y a
la vez cruciales para que una aplicacion sea de calidad y verdaderamente util.
La familia de lenguajes funcionales que responden a la estrategia de eval-
uacion conocida como call-by-value, en general toman un enfoque pragmatico,
adoptando algunos rasgos de los lenguajes imperativos. Simplemente tienen fun-
ciones que realizan muchas de estas tareas provocando efectos colaterales a su
ejecucion. Claro que dejan de ser funciones en el sentido matematico, pero en
la practica este enfoque ha probado funcionar, si tambien se tiene en cuenta la
especificacion del orden de evaluacion como parte del diseno. Esta es la forma
en la que la mayora de los lenguajes (funcionales o no) encaran los problemas
que implican interaccion con el mundo exterior.
Los lenguajes, como Haskell, que utilizan la estrategia de evaluacion call-
by-need, no pueden adoptar el enfoque anterior porque el orden de evaluacion
esta deliberadamente no-especificado. Una forma de mostrar este impedimento
es considerar el siguiente ejemplo:
xs = [printChar a, printChar b]
En un lenguaje call-by-value, evaluar esa ligadura tendra dos efectos colat-
erales: imprimir en pantalla la letra a y la letra b. Pero en Haskell, las llamadas
a printChar solo se ejecutaran si los elementos de la lista son evaluados. Por
ejemplo, si el unico uso de xs es en una llamada a length xs, entonces nada
sucedera en la pantalla, porque length no necesita evaluar los elementos de
una lista para conocer su longitud.
La conclusion es que la evaluacion perezosa y los efectos colaterales, desde
un punto de vista practico, son incompatibles.

Durante mucho tiempo esta situacion fue embarazosa para la comunidad


lazy, hasta que una solucion sorpresiva aparecio: las Monadas, un concepto
derivado de la Teora de Categoras, una de las ramas mas abstractas de la
matematica, que ha resultado ser la contribucion mas importante que Haskell
ha hecho al mundo de los lenguajes de programacion.
Las Monadas, permiten estructurar operaciones que deben ser realizadas en
un determinado orden, siendo la Entrada/Salida el campo donde mas utiles han
resultado ser. La idea consiste en que una funcion, si bien no puede directamente
provocar efectos colaterales, puede construir un valor que describa el efecto
colateral deseado que quien la llama debera provocar.

75.31 Teora de Lenguajes 15 [Link] cuatrimestre 2006


Haskell 16

Se puede pensar a una monada como una tupla de 3 elementos (M, return, bind)
donde M es un constructor de tipo13 y, tanto return como bind, son funciones
polimorficas cuya definicion puede expresarse as:

return : a M a
bind : M a (a M b) M b
Para que una instancia particular de lo anterior resulte consistente con el
marco teorico que le da vida, las definiciones de return y bind deben satisfacer
las siguiente 3 leyes:
(se adopta la notacion >>= para significar la version infija de bind)

return debe preservar toda la informacion de su argumento:

(return x) >>= f f x

m >>= return m

bind debe ser asociativa:

(m >>= f ) >>= g m >>= (x. (f x >>= g))

Donde:

f, g : a M a
m es una monada de la forma M a.
la notacion x. e corresponde a una funcion anonima que convierte val-
ores de x a la expresion e. La Seccion 10.7 esta dedicada a este tipo de
expresiones.
En palabras, el significado y objetivo de bind es secuenciar dos acciones
donde la segunda necesita conocer el resultado de la primera. Esa informacion
es de tipo a y viaja contenida en la monada. Por otro lado, el significado y
objetivo de return es servir de accion final en una secuencia de acciones14 .

En la seccion 15 se vera de que forma todo esto encaja en el sistema de tipos


de Haskell.

13 Puede considerarse a M como un contenedor tambien.


14 No es casualidad que su nombre signifique retornar.

75.31 Teora de Lenguajes 16 [Link] cuatrimestre 2006


Haskell 17

Parte III
Caractersticas Tecnicas
9. Sistema de Tipos
Haskell utiliza un sistema de chequeo estatico de tipos. Esto significa que a
cada expresion se le asigna un tipo en el momento de compilacion. En el caso
en el que una funcion, que espera un argumento de un cierto tipo, recibe uno de
tipo incorrecto, se producira un error en tiempo de compilacion impidiendo la
construccion del programa. Es importante destacar que la gestion de memoria
es realizada por un colector de basura y en ningun momento el programador se
ve involucrado en la reserva o liberacion explcita de la misma.

Haskell provee un conjunto de tipos basicos donde se pueden encontrar los


comunes a la mayora de los lenguajes: Bool, Char, String, Int, Integer, Float,
Double, Real y Floating.
Forman parte tambien del conjunto basico las n-Tuplas, arreglos de una can-
tidad fija de elementos heterogeneos: los pares (a, b), las ternas (a, b, c),
etc.

Las listas son quiza el tipo de dato mas utilizado en los lenguajes funcionales
y es por eso que, mas adelante, se les dedicaran las secciones 13.4 y 15.1.2. Lo
unico que se dira ahora es lo necesario para comprender algunos de los ejemplos
a continuacion: La notacion de una lista cuyos elementos son de tipo a , es [a].

9.1. Sinonimia de Tipos


Haskell provee un mecanismo para establecer tipos sinonimos. Resulta util
para renombrar tipos de uso muy comun que cobran un significado propio. A
continuacion, algunos ejemplos:
type Nombre = String
type Edad = Int
type Persona = (Nombre, Edad)
type Agenda = [Persona]

type Punto2D = (Floating, Floating)


type Punto3D = (Floating, Floating, Floating)
type Trayectoria2D = [Punto2D]
Debe quedar claro que un sinonimo no es un nuevo tipo. Todas aquellas
funciones que esperen recibir un argumento de tipo Agenda, funcionaran tambien
con argumentos del tipo [(Nombre, Edad)].

75.31 Teora de Lenguajes 17 [Link] cuatrimestre 2006


Haskell 18

10. Funciones
Siendo Haskell un lenguaje funcional, es natural que las funciones sean el
motor de todo. Un programa es una gran funcion cuyos calculos se definen me-
diante otras funciones, cuyas definiciones tambien dependen de otras funciones y
as hasta llegar a las primitivas del lenguaje. Siendo tan importantes, es espera-
ble que se provean multiples mecanismos simples para definirlas y manipularlas.
Como ya se comento en la Seccion 7, en Haskell las funciones son valores de
primer orden; estan al mismo nivel que los numeros, los caracteres o cualquier
otro tipo de dato.
Una funcion se define mediante ecuaciones. Las ecuaciones se componen de
dos partes, principalmente. La primera, es donde se especifica el nombre de la
funcion y sus argumentos; la segunda, contiene el cuerpo ejecutable. Un simple
ejemplo podra ser el siguiente:
duplicar x = x * 2
En el siguiente caso, se muestra una funcion que se define mediante multiples
ecuaciones:
opcion 1 = "Iniciar"
opcion 2 = "Cargar"
opcion 3 = "Grabar"
opcion 0 = "Salir"
opcion n = "Opcion incorrecta"

10.1. Pattern Matching


Es necesario a partir de ahora comprender que en Haskell todos los argu-
mentos se expresan como patrones. Al ser llamada la funcion, el compilador
comienza con la primer ecuacion hasta la ultima a probar si los argumentos
con los que ha sido invocada coinciden con alguno de los patrones. Una vez
encontrada la ecuacion, el compilador procede a ligar los identificadores libres
del patron. En el primer ejemplo de la Seccion 10, cualquier parametro coin-
cide con el patron y es ligado a x. En el segundo, una llamada a opcion 6,
solo coincidira con la ultima ecuacion, y n pasara a valer 6. Por el contrario,
una llamada a opcion 2, coincidira con la segunda ecuacion y no se seguira
probando con el resto.
Si, por ejemplo, se tiene una funcion que recibe una terna y se quiere devolver
alguno de sus elementos, se puede usar Pattern Matching para descomponerla
en sus componentes:
posicionX (x, y, z) = x
Es importante hacer notar que si bien esta funcion parecera tener tres argu-
mentos, guiandose por la sintaxis de otros lenguajes, es en realidad uno solo
pero expresado por un patron.

75.31 Teora de Lenguajes 18 [Link] cuatrimestre 2006


Haskell 19

Los parametros formales son tambien patrones, con la particularidad que


nunca fallan. Siempre es posible ligar el parametro x a cualquier expresion.
Los patrones que nunca fallan, como los parametros formales, se dice que
son irrefutables. En el mismo sentido, los que s pueden fallar se conocen como
refutables. Hay otros dos tipos de patrones irrefutables importantes: As-patterns
y Wild-cards.

10.1.1. As-patterns
Es a veces conveniente darle un nombre al patron para referirse a el en la
parte derecha de una ecuacion. Por ejemplo, en una funcion de la forma:
f (x, y) = if x == 0 then (y, y) else (x, y)
se puede ver que (x, y) aparece una vez como patron (a la izquierda de la
ecuacion), y otra vez como expresion (a la derecha). Para mejorar la legibilidad
(o para otros fines) es preferible usar la siguiente tecnica:
f s@(x, y) = if x == 0 then (y, y) else s
donde se a s se la liga a todo el patron mediante el smbolo @.
Estos patrones siempre coinciden, mas alla de que los subpatrones puedan
fallar.

10.1.2. Wild-cards
Otra situacion comun es cuando se desea construir un patron con partes
irrelevantes. Por ejemplo, una funcion que reduce una terna a un par, no necesita
conocer al ultimo elemento:
f (x, y, _) = (x, y)
Puede interpretarse al smbolo _ como un comodn que puede coincidir con
cualquier forma, pero no establece ningun tipo de ligadura. Es por eso que
pueden usarse multiples veces en un mismo patron sin provocar ambiguedades,
cosa que no es posible con los parametros formales.

10.1.3. Construcciones case of


En Haskell no se cumple la premisa que dice que todo lo que se puede
hacer con un case, se puede hacer con un if. S se cumple la inversa. La gran
diferencia es que en los primeros pueden utilizarse patrones y en los segundos
solo expresiones booleanas.
El Pattern matching provee un mecanismo de consignar el control basandose
en las propiedades estructurales de un valor. Cada una de las ecuaciones que
definen una funcion presentan un patron distinto y significan un camino a seguir
distinto. No siempre es deseable que cada nuevo patron posible introduzca una
nueva ecuacion, es por eso que Haskell posee una forma de expresar esto de una
forma mas concisa, las construcciones case of.
Una definicion de la forma:

75.31 Teora de Lenguajes 19 [Link] cuatrimestre 2006


Haskell 20

f pat11 ... pat1k = e1


...
f patn1 ... patnk = en
donde cada patij es un patron, es semanticamente equivalente a:

f x1 x2 ... xk = case (x1, ..., xk) of


(pat11, ..., pat1k) -> e1
...
(patn1, ..., patnk) -> en

10.2. Variables locales


Es a veces necesario simplificar el cuerpo de una funcion definiendo variables
auxiliares que representan expresiones repetidas. Haskell provee dos formas de
hacer esto: los espacios let y where. A continuacion se muestra el clasico ejemplo
de las races de un polinomio de segundo grado:
raices a b c =
let det = sqrt (b*b - 4*a*c)
dos_a = 2*a
in
((-b + det) / dos_a, (-b - det) / dos_a)
A veces es mas comodo dejar las variables para el final:
raices a b c = ((-b + det) / dos_a, (-b - det) / dos_a)
where det = sqrt (b*b - 4*a*c)
dos_a = 2*a

10.3. Guardas
Una alternativa a la hora de definir funciones partidas, es hacerlo mediante
guardas. Esta forma es mucho mas parecida a la forma en la que se definen
funciones partidas en la matematica. Por ejemplo, la sucesion de Fibonacci,
cuya definicion es la siguiente:

0 para n = 0
f (n) = 1 para n = 1
f (n 2) + f (n 1) para n > 1

Puede traducirse a Haskell de la siguiente manera:


fib n
| n == 0 = 0
| n == 1 = 1
| n >= 2 = fib (n-1) + fib (n-2)

75.31 Teora de Lenguajes 20 [Link] cuatrimestre 2006


Haskell 21

Cada guarda es una condicion booleana. Puede suceder que el compilador


encuentre la ecuacion cuyo patron coincide con el parametro y al entrar a la
definicion, los guardas no le permitan ejecutar ninguna sentencia. En este caso
el compilador descartara la ecuacion y continuara buscando. Dependiendo de
como se ordenen, puede emitir una advertencia de solapamiento de patrones.

10.4. Tipo de las funciones


Uno de los lugares donde Haskell justifica ser matematicamente elegante es
en la notacion de los tipos de las funciones. As como en las matematicas el
tipo de una funcion se expresa f : A B, en donde A y B son conjuntos de
valores (tipos), en Haskell se expresa f :: a -> b, donde a y b representan
tipos genericos.
He aqu algunos ejemplos:
esMayuscula? :: Char -> Bool
esPar? :: Integer -> Bool
sumaElementos :: [Integer] -> Integer
losDosPrimeros :: [Integer] -> (Integer, Integer)
Cuando la funcion recibe mas de un parametro, la notacion se extiende de
la siguiente manera:
letraEsta? :: Char -> String -> Bool
sonElMismo? :: Integer -> Integer -> Bool
uneListas? :: [Integer] -> [Integer] -> [Integer]

10.5. Funciones polimorficas


En los ejemplos anteriores solo se vieron definiciones sobre tipos especficos.
Es muy comun que las funciones operen sobre mas de un tipo o sobre estructuras
que contienen tipos irrelevantes para la operacion en cuestion. Estas funciones
se conocen como polimorficas, pues su tipo es valido para muchas formas. Los
clasicos ejemplos son las operaciones sobre listas. A continuacion se muestra el
tipo de varias funciones que operan sobre listas:
length :: [a] -> Int
map :: (a -> b) -> [a] -> [b]
foldr :: (a -> b -> b) -> b -> [a] -> b
filter :: (a -> Bool) -> [a] -> [a]
Donde a y b representan tipos genericos.
Otro ejemplo mas interesante, es el de una funcion que devuelve la composi-
cion de dos funciones que recibe como parametro:
componer :: (b -> c) -> (a -> b) -> a -> c

75.31 Teora de Lenguajes 21 [Link] cuatrimestre 2006


Haskell 22

10.6. Currying
Currying es la tecnica de transformar una funcion cuyos argumentos estan
contenidos en un patron de la forma (x, y, ..., z) a una funcion donde los
argumentos son independientes. Haskell provee una funcion llamada curry cuyo
tipo es:
curry :: ((a, b) -> c) -> a -> b -> c
Intuitivamente, se puede pensar que si a una funcion se le fijan algunos argu-
mentos, el resultado es una funcion de los argumentos restantes. Esto permite
construir nuevas funciones mas simples a partir de otras mas complejas. Por
ejemplo, si se quiere construir una funcion que, dada una lista de enteros, de-
vuelva una lista con los cuadrados de cada entero, puede usarse una aplicacion
parcial (o seccion, en la jerga) de la funcion map:
elevarListaAlCuadrado = map (^2)
donde se omite el ultimo parametro. Es interesante ver el tipo de esta funcion:
elevarListaAlCuadrado :: [Integer] -> [Integer]

10.7. Expresiones Lambda


Ha llegado el momento de mostrar otra de las insignias de Haskell, las expre-
siones o abstracciones Lambda. Si bien ya existan varios lenguajes funcionales
que las incluan, en Haskell juegan un rol mas cotidiano. La equivalencia es casi
inmediata y sera la siguiente:
Definicion: x. e \x -> e
Especializacion: (x. e)a (\x -> e)a
Ejemplo: (x. x + 15)a \x -> x + 15
Ejemplo con mas parametros: (x y. x + y)a \x y -> x + y
En este contexto, currificar el ultimo ejemplo sera:
\x y -> x + y \x -> \y -> x + y
Esto es, se transforma una funcion de dos argumentos que devuelve una
expresion, a una funcion que recibe un argumento y devuelve una funcion que
toma un argumento (el restante) y devuelve una expresion.

Uno de los usos mas comunes que se le da a estas expresiones, es la de


construir funciones anonimas para ser usadas en lugares muy especficos que
no justifican la creacion de una nueva funcion con nombre propio. Un ejemplo
tpico es al usar la funcion map, construir la funcion a aplicar en el mismo lugar
de la llamada:
listaDesplazada l = map (\x -> x + 1) l

75.31 Teora de Lenguajes 22 [Link] cuatrimestre 2006


Haskell 23

11. Layout
Haskell usa una sintaxis de dos dimensiones llamada layout que se basa en
organizar las declaraciones alineadas en columnas. Se dice tambien que Haskell
tiene una sintaxis layout-driven.
Es importante recordar, en primer lugar, que el proximo caracter que le sigue
a cualquiera de las palabras reservadas where, let o of es el que determina la
columna de inicio para las declaraciones que vaya a contener. En segundo lugar,
hay que asegurar que la primer columna de un bloque este mas a la derecha que
la primer columna del bloque inmediatamente abarcante, pues de otra manera
sera ambiguo). El fin de un bloque se alcanza cuando se encuentra una lnea
mas a la izquierda que la primera lnea del bloque en cuestion.
Es altamente recomendable manipular los fuentes de Haskell desde editores
con fuentes monoespaciadas para evitar errores de parsing.

75.31 Teora de Lenguajes 23 [Link] cuatrimestre 2006


Haskell 24

12. Inferencia de Tipos


Otra de las caractersticas que hacen al sistema de tipos de Haskell especial
es la capacidad de inferir el tipo de una expresion sin necesidad de que el pro-
gramador lo especifique. Esto lo logra analizando las operaciones a las que una
expresion esta sometida.
El sistema asigna el tipo menos general que contenga todas las instancias
posibles de la expresion. Por ejemplo, infiere que el tipo de la funcion tail es
[a] -> [a]. Sin embargo, los tipos [a] -> [b], a -> [b], [a] -> b, a -> b y
hasta inclusive a son validos; pero todos ellos, en mayor o menor medida, son mas
generales de lo estrictamente necesario. Por el contrario, [Integer] -> [Integer]
o [Char] -> [Char] seran demasiado especficos y no estaran contemplando
todos los casos posibles.
La existencia de tipos principales unicos es la caracterstica mas importante
del sistema de tipos conocido como Hindley-Milner, que forma parte de la base
de Haskell, ML, Miranda y otros lenguajes.

75.31 Teora de Lenguajes 24 [Link] cuatrimestre 2006


Haskell 25

13. Tipos de datos algebraicos


Los Tipos de datos algebraicos son tipos de datos donde cada uno de sus
valores son un tipo de dato envuelto en uno de sus constructores. Por ejemplo,
se podra construir el tipo de dato ColorRGB a partir de 3 enteros de la siguiente
manera:
data ColorRGB = RGB Int Int Int
Aqu, RGB juega el rol de (unico) constructor que recibe como parametro a los
tipos base a partir de los cuales se generara el tipo de dato algebraico.
Un ejemplo de uso sera la creacion de funciones que los generen:
rojo = RGB 255 0 0
verde = RGB 0 255 0
azul = RGB 0 0 255
amarillo = RGB 0 255 255
gris n = RGB n n n
Es importante destacar que los constructores no se ejecutan y la unica forma
de obtener los datos que envuelven es mediante Pattern Matching. Por ejemplo,
si se quisiera obtener la composicion de un ColorRBG, se podran construir las
siguientes funciones:
cuantoRojo, cuantoVerde, cuantoAzul :: ColorRGB -> Int
cuantoRojo (RGB r g b) = r
cuantoVerde (RGB r g b) = g
cuantoAzul (RGB r g b) = b
Tambien es posible definir un tipo de dato algebraico a partir de multiples
constructores. Por ejemplo, un tipo de dato que modele alumnos y docentes:
data Persona = Docente String Integer | Alumno String Integer
y una funcion que mediante Pattern Matching defina un comportamiento dis-
tinto para cada caso:
informacion :: Persona -> String
informacion (Docente n i) = "Docente " ++ n ++ " - id: " ++ (show i)
informacion (Alumno n p) = "Alumno " ++ n ++ " - Padron: " ++ (show p)

13.1. Campos etiquetados


El ejemplo anterior donde se definio el tipo Persona, solo posee dos campos
y no presentan dificultad para ser accedidos posicionalmente. Si se utilizara un
tipo mas completo, por ejemplo el siguiente:
type Nombre = String
type Apellido = String
type Edad = Integer

75.31 Teora de Lenguajes 25 [Link] cuatrimestre 2006


Haskell 26

type Padron = Integer


type Email = String
type CBC = Bool

data Alumno = Alumno Nombre Apellido Edad Padron Email CBC

getNombre :: Alumno -> Nombre


getNombre (Alumno n _ _ _ _ _) = n

getEdad :: Alumno -> Edad


getEdad (Alumno _ _ e _ _ _) = e

getEmail :: Alumno -> Email


getEmail (Alumno _ _ _ _ e _) = e

se observa como aumenta la posibilidad de confundir el orden de los campos a


medida que aumentan en cantidad.
Para evitar esto, Haskell permite etiquetar los campos para luego poder
acceder a ellos por su nombre. El ejemplo anterio, podra reescribirse de la
siguiente manera:
type Nombre = String
type Apellido = String
type Edad = Integer
type Padron = Integer
type Email = String
type CBC = Bool

data Alumno = Alumno {nombre :: Nombre,


apellido :: Apellido,
edad :: Edad,
email :: Email,
padron :: Padron,
cbc :: CBC}

getNombre :: Alumno -> Nombre


getNombre a = nombre a

getEdad :: Alumno -> Edad


getEdad a = edad a

getEmail :: Alumno -> Email


getEmail a = email a
donde cada campo tiene un nombre, y ademas se han definido, implcitamente,
los siguientes selectores:

75.31 Teora de Lenguajes 26 [Link] cuatrimestre 2006


Haskell 27

nombre :: Alumno -> Nombre


apellido :: Alumno -> Apellido
edad :: Alumno -> Edad
email :: Alumno -> Email
padron :: Alumno -> Padron
cbc :: Alumno -> CBC
El ejemplo siguiente ilustra como construir nuevos valores, utilizando campos
etiquetados:
francisco = Alumno {nombre = "Francisco",
apellido = "Albani",
edad = 22,
email = "falbani@[Link]",
padron = 84891,
cbc = true}
Si se omiten algunos campos, el resto queda indefinido.
Para construir patrones, se usa una sintaxis muy similar a la del constructor.
Por ejemplo:
getNombre :: Alumno -> Nombre
getNombre (Alumno {nombre=n}) = n

getEdad :: Alumno -> Edad


getEdad (Alumno {edad=e}) = e

getEmail :: Alumno -> Email


getEmail (Alumno {email=e}) = e
Una funcion puede utilizar los campos etiquetados de una estructura ex-
istente para construir una nueva a partir de ella. En el siguiente ejemplo, se
actualizala edad de un alumno:
franciscoActualizado = francisco {edad = 1 + edad francisco}
Por supuesto que no es una actualizacion destructiva: simplemente se crea una
nueva copia tomando por defecto los valores del dato origen.

Esta forma alternativa de expresar un Tipo Algebraico no altera su natu-


raleza. Es una va para hacerlos mas manejables y extendibles, dado que de esta
manera se pueden agregar o quitar nuevos campos sin necesidad de actualizar
todas las referencias al constructor.

13.2. Enumerados
Un caso especial son los tipos de datos algebraicos cuyos constructores no
envuelven ningun tipo. Su uso principal es para construir enumerados. En el

75.31 Teora de Lenguajes 27 [Link] cuatrimestre 2006


Haskell 28

siguiente ejemplo se pretende nuclear a todos los continentes bajo un mismo


tipo15 :
data Continente = America | Europa | Africa | Asia | Oceania
Una funcion que podra hacer uso de Continente sera, por ejemplo, conectadosTierra,
que, dados dos continentes, informa si estos estan o no conectados por tierra:
conectadosTierra :: Continente -> Continente -> Bool
conectadosTierra America _ = False
conectadosTierra Oceania _ = False
conectadosTierra Europa Asia = True
conectadosTierra Europa _ = False
conectadosTierra Asia Africa = True
conectadosTierra Asia Europa = True
conectadosTierra Asia _ = False
conectadosTierra Africa Asia = True
conectadosTierra Africa _ = False

13.3. Recursivos
Dada la definicion de tipo de dato algebraico en la Seccion 13, nada impide
utilizar como parametro de un constructor, al propio tipo que se esta definiendo.
Esta propiedad recursiva, permite concebir estructuras virtualmente infinitas.
Mediante esta tecnica podemos representar al clasico ejemplo de los arboles
binarios definidos como, o bien vacos, o constituidos por un nodo que envuelve
un dato y dos ramificaciones:
data ArbolBinario = ArbolVacio | Nodo Int ArbolBinario ArbolBinario
La siguiente funcion recursiva, dado un arbol, devuelve su profundidad:
profundidad :: ArbolBinario -> Integer
profundidad ArbolVacio = 0
profundidad (Nodo l r) = 1 + max (profundidad l) (profundidad r)

13.4. Polimorficos
En muchos casos, el concepto que se esta abstrayendo resulta ser una de-
scripcion general que envuelve a varios tipos distintos. El ejemplo por excelencia
son las Listas, donde practicamente todas las operaciones que se realizan sobre
las mismas no imponen condiciones sobre el tipo a contener. Vale la pena decir
que tambien son el ejemplo por excelencia de las estructuras recursivas poten-
cialmente infinitas (Seccion 13.3). La definicion mas comun es:
data Lista a = ListaVacia | Cons a (Lista a)
Otro ejemplo muy comun son los arboles binarios genericos:
15 Se ha dejado de lado el rigor geografico para simplificar el ejemplo.

75.31 Teora de Lenguajes 28 [Link] cuatrimestre 2006


Haskell 29

data ArbolBinario a = ArbolVacio | Nodo a (ArbolBinario a) (ArbolBinario a)


Es oportuno destacar que, al ser las listas el tipo de dato de uso mas natural
en Haskell, se provee de azucar sintactico para facilitar su creacion y manipu-
lacion. A continuacion se muestran las equivalencias con la definicion anterior:
Lista a [a]
ListaVacia []
Cons a (Lista a) a:[a]

75.31 Teora de Lenguajes 29 [Link] cuatrimestre 2006


Haskell 30

14. Clases de tipos


La inclusion de Clases de tipos en el disenoo de Haskell fue una de las deci-
siones mas acertadas del comite, ya que es considerada una de sus caractersticas
mas distintivas. Fueron concebidas por Philip Wadler en 1988 durante una con-
versacion con Joe Fasel acerca de como la sobrecarga deba estar reflejada en
el tipo de una funcion. Wadler entendio erroneamente la idea de Fasel, y ese
fue el momento en el que las Clases de tipos nacieron. Tiempo despues, Steven
Blott, alumno de Wadler, ayudo a formular las reglas y a probar la coherencia,
sanidad y completitud del sistema, para su tesis doctoral. El comite acepto la
idea sin demasiado debate, en contradiccion directa con el objetivo de solo in-
cluir caractersticas muy bien probadas y que gozaran de gran consenso.
La motivacion detras de la creacion de las Clases de tipos era solucionar un
importante problema: sobrecarga de operadores para operaciones numericas y
equivalencia16 . Para ilustrar lo anterior, se puede considerar la funcion elem,
que sirve para saber si una lista contiene a un elemento dado. Su definicion es
la siguiente:
x elem [] = False
x elem (y:ys) = x==y || (x elem ys)
A partir de esto, se esperara que el tipo de elem sea a -> [a] -> Bool. Pero
eso implicara que el tipo de (==) sea a -> a -> Bool, inclusive cuando hay
tipos para los cuales el concepto de igualdad no esta bien definido, y es voluntad
del programador que sobre ellos no este definido el operador (==). Mas alla de
esto, aun suponiendo que el concepto de igualdad estuviera bien definido para
todos los tipos, su computo depende de cada uno en particular y debera existir
un mecanismo para especificarlo.

14.1. Polimorfismo Ad-Hoc


Las Clases de tipo son el mecanismo que Haskell provee para soportar el
Polimorfismo Ad-Hoc, mas conocido como sobrecarga. Este concepto permite
que el comportamiento de una misma operacion pueda ser diferente para cada
tipo de dato distinto. Permite definir un conjunto de operaciones y agruparlas
bajo un nombre de clase del que luego se especificara que tipos son instancias
de la misma. En otras palabras, una clase define un conjunto de reglas que un
tipo debe cumplir para considerarse instancia de esa clase.
El ejemplo por excelencia es la clase que define a los tipos de datos cuyos
elementos pueden o no ser equivalentes. Su nombre es Eq y a continuacion se
muestra su definicion17 :
16 Con el correr del tiempo, el concepto se fue generalizando hasta convertir a Haskell en una

especie de laboratorio en el cual numerosas extensiones al sistemas de tipos han sido disenadas
e implementadas.
17 En realidad, la definicion es un poco mas completa, pero a los efectos de simplificar la

explicacion, se han omitido algunos detalles.

75.31 Teora de Lenguajes 30 [Link] cuatrimestre 2006


Haskell 31

class Eq a where
(==) :: a -> a -> Bool
Lo anterior se puede leer de la siguiente forma: Para cada tipo a que sea una
instancia de la clase Eq , (==) es de tipo a -> a -> Bool. Simbolicamente:

(==) :: (Eq a) => a -> a -> Bool


Se puede ver que ahora, queda explcito en el tipo de la funcion la restriccion a
la que sus parametros son sometidos.
Ahora se podra, por ejemplo, hacer que el tipo de dato Arbol, sea una
instancia de Eq de la siguiente manera:

instance (Eq a) => Eq (Arbol a) where


Hoja a == Hoja b = (a == b)
(Rama l1 r1) == (Rama l2 r2) = (l1==l2) && (r1==r2)
_ == _ = False
Que se lee: Para cada tipo a que sea una instancia de la clase Eq, el tipo Arbol a
es tambien una instancia de la clase Eq, a partir de la definicion dada del oper-
ador (==). La definicion de (==) es llamada tambien metodo. Es importante
notar la restriccion extra que se impone sobre el tipo a. Esencialmente dice que
la comparacion de igualdad entre arboles de elementos de tipo a, solo sera posi-
ble si estos ultimos admiten tal comparacion. Estas restricciones se conocen
como Contexto. Si se omiten, pueden surgir errores de tipado estatico.
Es posible tambien, en la definicion de una clase, establecer metodos por
defecto. Por ejemplo, una definicion mas completa de la clase Eq es:
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
x /= y = not (x == y)
donde se agrega una operacion de desigualdad con un comportamiento por de-
fecto. Si una declaracion de instancia de tipo omite la definicion de (/=), esta
sera interpretada como la negacion de (==).

14.2. Extension de clases


Haskell tambien soporta la nocion de extension de clases. Por ejemplo, se
podra definir una clase de tipo ordinal, Ord, que herede todas las operaciones
de la clase Eq, y agregue un conjunto de operaciones que permitan expresar los
conceptos de mayor, menor, mnimo y maximo. He aqu su definicion:
class (Eq a) => Ord a where
(<), (<=), (>=), (>) :: a -> a -> Bool
max, min :: a -> a -> a

75.31 Teora de Lenguajes 31 [Link] cuatrimestre 2006


Haskell 32

Se puede interpretar como que Eq es una superclase de Ord o como que Ord
es una subclase de Eq. Cualquier tipo que sea una instancia de Ord debe ser
tambien una instancia de Eq.
Los metodos de una subclase pueden asumir la existencia de metodos de una
superclase. Por ejemplo, una declaracion mas completa de Ord podra contener
este metodo por defecto para (<):
x < y = x <= y && x /= y
donde se supone la existencia de (/=).
A partir de esto, se puede ver que el tipo de la funcion quicksort:

quicksort :: (Ord a) => [a] -> [a]


refleja explcitamente que solo puede operar sobre listas de elementos ordinales.

14.3. Instancias derivadas


Escribir instancias de Eq, Ord y varias clases mas es en general algo obvio
y puede resultar molesto. Afortunadamente, Haskell ofrece un mecanismo para
automatizar esta tarea. Si el tipo de dato es lo suficientemente simple, el com-
pilador puede derivar automaticamente varias de las clases mas comunes. Un
ejemplo sera:
data Alumno = Alumno {nombre :: Nombre,
apellido :: Apellido,
edad :: Edad,
email :: Email,
padron :: Padron,
cbc :: CBC} deriving Eq
donde se especifica que el tipo Alumno debe ser instancia de la clase Eq, mediante
la orden deriving.

14.4. Comparacion con otros lenguajes


Los metodos definidos en una clase de Haskell corresponden a funciones
virtuales en una clase de C++. Cada instancia de la clase provee su propia
definicion para cada metodo.
Las clases de Haskell son ligeramente similares a las interfaces de Java en
el sentido siguiente: se define un protocolo de uso en lugar de definir su
implementacion.
Haskell no soporta el estilo de sobrecarga de C++ en donde funciones
con argumentos de distintos tipos pueden compartir el mismo nombre.
En Haskell no hay una clase universal base como por ejemplo Object en
Java.

75.31 Teora de Lenguajes 32 [Link] cuatrimestre 2006


Haskell 33

En Haskell no hay control de acceso, como por ejemplo los modificadores


public o private. En lugar de eso, se debe usar el Sistema de Modulos
para ocultar los componentes de una clase.

75.31 Teora de Lenguajes 33 [Link] cuatrimestre 2006


Haskell 34

15. Las monadas como una clase de tipo


Las monadas en Haskell son una clase de tipo como cualquier otra, cuya
definicion es la siguiente:
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
Donde (>>) es una version mas simple de (>>=) donde el resultado de la primer
accion no es necesitado por la segunda y por eso es descartado.
Al instanciarla se debe garantizar el cumplimiento de las leyes expuestas en
la Seccion 8.
A parte de esas tres leyes, algunas monadas tambien obedecen leyes de adi-
cion. Tienen un valor especial llamado mzero y un operador mplus que obedecen
lo siguiente:

mzero >>= f mzero


m >>= (x mzero) mzero
mzero mplus m m
m mplus mzero m

Para representar a las monadas con propiedades de adicion, Haskell provee


una clase derivada de Monad:
class (Monad m) => MonadPlus m where
mzero :: m a
mplus :: m a -> m a -> m a

15.1. Las monadas mas ilustres


El Preludio18 de Haskell incluye varias instancias de la clase Monad. A con-
tinuacion se hara un repaso de las mas ilustres:

15.1.1. Maybe
Definicion: data Maybe a = Just a | Nothing

Este tipo encapsula un valor opcional. Un valor de tipo Maybe a o bien con-
tiene un valor de tipo a, representado por el constructor Just a, o esta vaco,
representado por el constructor Nothing. Es util para trabajar en situaciones
donde una operacion puede resultar inconsistente, sin necesidad de tomar me-
didas drasticas como levantar un error.
18 Se conoce como Prelude al paquete basico de funciones de Haskell.

75.31 Teora de Lenguajes 34 [Link] cuatrimestre 2006


Haskell 35

Como una monada, puede ser vista como una estrategia para secuenciar ac-
ciones asociadas a un tipo que pueden fallar y no devolver nada. Su instanciacion
es la siguiente:
instance Monad Maybe where
return = Just
fail = Nothing
Nothing >>= f = Nothing
(Just x) >>= f = f x

instance MonadPlus Maybe where


mzero = Nothing
Nothing mplus x = x
x mplus _ = x

15.1.2. Las listas tambien son monadas


La monada Lista representa la estrategia de combinar una cadena de computos
no-determinsticos aplicando operaciones a todos los posibles valores en cada pa-
so. Es muy util para resolver ambiguedades. Su instanciacion es la siguiente:
instance Monad [] where
m >>= f = concatMap f m
return x = [x]
fail s = []

instance MonadPlus [] where


mzero = []
mplus = (++)

[Link]. Comprension de Listas Otro de los lugares donde Haskell mues-


tra su elegancia matematica es en lo que se conoce como List Comprehension,
azucar sintactico de una construccion monadica que permite generar y expresar
conjuntos de una forma muy similar a como se lo hace en la matematica. A
continuacion, se listas varios ejemplos significativos:
Los enteros comprendidos entre 1 y 10: [1..10]

[1,2,3,4,5,6,7,8,9,10]

Los enteros positivos: [1..]

[1,2,3,4,5,6,7,8,9,10,11,12,13,14...

Un conjunto de pares: [(x, y) | x <- [1..3], y <- [4..5]]

[(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)]

75.31 Teora de Lenguajes 35 [Link] cuatrimestre 2006


Haskell 36

Los enteros positivos pares: [n | n <- [1..], n mod 2 == 0]

[2,4,6,8,10,12,14,16...

Las cuadrados de los 15 primeros enteros: [n*n | n <- [1..15]]

[1,4,9,16,25,36,49,64,81,100,121,144,169,196,225]

Pares (x, f (x)) donde x pertenece al conjunto s: [(x, f x) | x <- s]



S = { n | n N, n > 100 }: S = [ n | n<-[0..], sqrt n>100 ].
A partir de los ejemplos anteriores puede verse que a la izquierda del smbolo
| se expresa la forma de los resultados deseados, y a la derecha se enumeran,
separados por coma, los generadores y las condiciones que se deben cumplir,
conocidas como guardas.

Para finalizar, algunos ejemplos clasicos mas complejos:

Lista infinita de numeros primos utilizando el algoritmo de la Criba de


Eratostenes:

primos = sieve [2 .. ]
where sieve [] = []
sieve (x:xs) = x : sieve [y | y <- xs, y mod x > 0]

Simple implementacion del algoritmo de ordenamiento QuickSort, muy


famosa por ocupar solo 2 lneas:

qs [] = []
qs (x:xs) = qs ([c|c <- xs,c <= x]) ++ [x] ++ qs ([c|c <- xs,c > x])

Funcion para obtener una lista de los numeros perfectos19 menores que n:

pn n = [p | p <-[1..n], (p == sum [f | f <-[1..(p-1)], (p mod f) == 0])]

15.1.3. IO Monad
Esta es sin duda la mas significativa pues ha sido la puerta de entrada de
las monadas al mundo de la programacion. Su llegada ha trado luz a un sector
oscuro de la programacion funcional, cual mesas a un pueblo a la deriva.
Como se ha visto en la Seccion 8, los lenguajes funcionales puros son incom-
patibles con la Entrada/Salida debido a su opacidad referencial. Esta incom-
patibilidad se resuelve confinando los computos que realicen I/O dentro de la
monada. Al no proveerse mecanismos que permitan remover los datos contenidos
19 Un numero perfecto es un entero con la propiedad de ser igual a la suma de los divisores

propios menores que el mismo.

75.31 Teora de Lenguajes 36 [Link] cuatrimestre 2006


Haskell 37

por ella, se dice que es una monada sin salida: los efectos colaterales quedan
aislados del resto funcional puro del programa. Las acciones se van ejecutando
a medida que se van ligando, permitiendo secuenciar una serie de computos en
un estilo que emula muy bien al de la programacion imperativa.
En Haskell, la funcion con la cual se dispara la ejecucion, main, debe ten-
er el tipo IO (). Es por esto que los programas en Haskell, son tpicamente
estructurados como una secuencia de acciones I/O de estilo imperativo en el
nivel mas alto, con llamadas al codigo funcional puro. Las funciones del modu-
lo IO no realizan tareas de I/O por su cuenta, pero devuelven acciones I/O,
que describen la operacion deseada. Estas acciones pueden ser combinadas den-
tro de la monada IO (de una manera funcional pura) pra crear acciones I/O
mas complejas, resultando en la accion I/O final que es el valor principal del
programa.
La definicion de IO es dependiente de la plataforma; pero aun as, resulta
util tener una idea de como es su instancia a la clase Monad:
instance Monad IO where
return a = ... -- Funcion de tipo: a -> IO a
m >>= k = ... -- Ejecuta la accion IO m y liga el valor ..
fail s = ... -- .. que contiene a la entrada de k
A continuacion, se mostrara un pequeno programa que pide al usuario que
ingrese un texto, para luego invertirlo y mostrarlo. Este programa responde
a un esquema muy simple de entrada-procesamiento-salida. El objetivo es
mostrar como, efectivamente, la funcion main es el resultado de la combinacion
de acciones que, sin salir de la monada IO, interactuan con el exterior para
obtener y entregar datos, y con el interior funcional puro para procesarlos:
escribirLinea :: [Char] -> IO ()
escribirLinea [] = (putChar \n) >> return ()
escribirLinea (c:cs) = (putChar c) >> (escribirLinea cs)

leerLinea :: IO [Char]
leerLinea = getChar >>= \c ->
if c == \n then
return []
else
leerLinea >>= \cs ->
return (c : cs)

main :: IO ()
main = escribirLinea "Ingrese el texto: " >>
leerLinea >>= \s ->
escribirLinea (reverse s)
El Preludio de Haskell provee funciones para leer y escribir lneas pero, por cues-
tiones didacticas se opto por crear nuevas con implementaciones bien explcitas,
para enriquecer el ejemplo.

75.31 Teora de Lenguajes 37 [Link] cuatrimestre 2006


Haskell 38

Es notable el aspecto imperativo de la funcion main. La funcion reverse


forma parte del Preludio y juega el rol de codigo funcional puro encargado de
procesar los datos.

[Link]. Do-notation 20
Otra conclusion que se cae de madura del ejemplo de la seccion anterior,
es que los operadores (>>=) y (>>) junto a las expresiones lambda anonimas
se vuelven mas y mas intrusivas a medida que el codigo cobra tamano. Es por
eso que Haskell provee azucar sintactico para poder escribir las secuencias de
computos en un estilo imperativo mas ordenado, emulando inclusive a la asig-
nacion de variables. El resultado de un computo monadico puede ser asignado a
una variable usando el operador <-. Luego, al usar esa variable en un computo
subsecuente, automaticamente se realiza la ligadura. El tipo de la expresion a
la derecha de <- es monadico, m a. La expresion de la izquierda debe ser un
patron para enfrentar con el valor dentro de la monada.
La traduccion de la notacion comun a la notacion do indica que cada con-
struccion de la forma e1 >>= \x -> puede escribirse como x <- e1 y cada una
de la forma de e2 >>= \_ -> se transforma en, simplemente e2.

A continuacion se expondra una nueva version del programa anterior donde


se utiliza la notacion do:

escribirLinea :: [Char] -> IO ()


escribirLinea [] = do putChar \n
return ()
escribirLinea (c:cs) = do putChar c
escribirLinea cs

leerLinea :: IO [Char]
leerLinea = do c <- getChar
if c == \n then return []
else do cs <- leerLinea
return (c : cs)

main = do escribirLinea "Ingrese el texto: "


s <- leerLinea
escribirLinea (reverse s)

20 Si bien la presentacion de este concepto esta en el contexto de la monada IO, no debe

olvidarse que puede utilizarse con cualquier otra.

75.31 Teora de Lenguajes 38 [Link] cuatrimestre 2006


Haskell 39

16. Modulos
Un programa tpico escrito en Haskell se forma a partir de la union de
modulos. Este concepto sirve para controlar los espacios de nombres y como
mecanismo para crear tipos de datos abstractos (ADTs).
Un modulo puede incluir cualquiera de las declaraciones mostradas anteri-
ormente: funciones, tipos de datos, clases, instancias, sinonimias, etc. El unico
requisito que existe en cuanto al orden de aparicion, es el que obliga a realizar
todas las importaciones al principio del modulo.
Las implementaciones mas comunes de compiladores de Haskell asocian a
cada modulo un archivo. Por ejemplo, un modulo destinado a nuclear funciones
de numeros complejos, estara contenido en el archivo [Link]. Un modu-
lo que quiera hacer uso de el, debera importarlo al principio con la sentencia
import Complejos. A continuacion, un ejemplo de archivo [Link]:
module Complejos where

type Complejo = (Float, Float)

conjugado :: Complejo -> Complejo


conjugado (a, b) = (a, b*(-1))

re, im, modulo :: Complejo -> Float


re (a, b) = a
im (a, b) = b
modulo (a, b) = sqrt (a*a + b*b)

suma, mult :: Complejo -> Complejo -> Complejo


suma (a, b) (c, d) = (a+c, b+d)
mult (a, b) (c, d) = (a*c - b*d, a*d + c*b)
y un ejemplo de modulo que lo utilice:
import IO
import Complejos

main = do putStrLn "Ingrese la parte real: "


r <- getLine
putStrLn "Ingrese la parte imaginaria: "
i <- getLine
putStrLn ("El modulo de " ++ show r ++ " + " ++ show i ++
"i es: " ++ show (modulo (4, 3)) ++ ".")
En este caso, la llamada import Complejos traetodas las definiciones de
[Link] al modulo. Existen mecanismos para limitar esto y pueden estar
tanto en el modulo origen como en el modulo destino. En el caso en el que
se desee importar solo algunas cosas de un modulo externo, las limitaciones se

75.31 Teora de Lenguajes 39 [Link] cuatrimestre 2006


Haskell 40

estableceran dentro del modulo destino; en el caso en el que se desee restringir el


acceso a un modulo (el caso de los ADTs) las limitaciones estaran en el modulo
origen.

16.1. Restriccion al importar


Para especificarle a import especficamente las cosas que tiene que importar,
hay que listarlas de la siguiente forma:
import Complejos (modulo, suma)

Es posible tambien especificar que cosas no exportar, utilizando la orden


hiding:
import Complejos hiding (suma, mult)

16.2. Restriccion al exportar (ADTs)


La unica forma de construir tipos abstractos de datos (ADTs) en Haskell,
es utilizando modulos con restricciones a la hora de exportar.
Un ejemplo simple, podra ser el caso de la creacion de un ADT Arbol. El
contenido del modulo que lo representara sera:
module TreeADT (Tree, leaf, branch, cell,
left, right, isLeaf) where

data Tree a = Leaf a | Branch (Tree a) (Tree a)

leaf = Leaf
branch = Branch
cell (Leaf a) = a
left (Branch l r) = l
right (Branch l r) = r
isLeaf (Leaf _) = True
isLeaf _ = False

donde se puede observer como, al momento de declarar el modulo, se omite en


la lista de exportacion a los dos constructores de Tree a.

75.31 Teora de Lenguajes 40 [Link] cuatrimestre 2006


Haskell 41

Parte IV
Conclusion
Aqu concluye la redaccion del informe de Haskell, lenguaje que me fuera
asignado para investigar como asignatura en la materia Teora de Lenguajes, el
segundo cuatrimestre del ano 2006.
Ha sido el fruto de largas horas de lectura de publicaciones teoricas, de
tutoriales practicos y de ensayos ludicos. Su realizacion me abrio las puertas
al mundo de los Lenguajes Funcionales, sector de la programacion que aun no
conoca y motivo de mucha intriga; practicamente todo lo expuesto en este
trabajo ha sido para mi nuevo.
Muchas cosas han quedado en el tintero por limitaciones, en parte externas,
como la falta de tiempo, y en parte propias, como la incapacidad de compren-
der suficientemente a fondo ciertos temas tan rapidamente. Hubiera querido
presentar algunas otras caractersticas de Haskell como por ejemplo Lazy pat-
terns, newtype, funciones infijas, patrones n+k. Tambien me hubiera gustado dar
informacion sobre los compiladores, interpretes y libreras disponibles; as co-
mo tambien confexionar una galera de aplicaciones famosas escritas en Haskell.
Ejemplos de monadas utilizadas para simular estado, excepciones y concurrencia
son quiza las gotas de tinta mas importantes que no he derramado.
Aun as, el producto final y el valor de los conocimientos adquiridos en la
cursada de la materia y en la investigacion me han dejado harto conforme.

Francisco Albani
Buenos Aires,
7 de Marzo de 2007.

75.31 Teora de Lenguajes 41 [Link] cuatrimestre 2006


Haskell 42

Parte V
Bibliografa
Referencias
[Bac78] John Backus.
Can programming be liberated from the von Neumann style?: a
functional style and its algebra of programs (1978)
[Link]
[Hug89] John Hughes.
Why Functional Programming Matters (1989)
[Link]
[Pey00] Simon Peyton Jones.
Tackling the awkward squad (2000)
[Link]
history-of-haskell/[Link].
[His07] Paul Hudak, John Hughes, Simon Peyton Jones & Philip Wadler.
A History of Haskell: being lazy with class (2007)
[Link]
history-of-haskell/[Link].
[Haskellorg] Haskell Community.
Haskell Official Site
[Link]
[HaskellIrc] Haskell IRC Users.
Haskell IRC
[Link]
[Gen00] Paul Hudak, John Peterson & Joseph Fasel.
A Gentle Introduction to Haskell
[Link]
[Ros06] Andrea Rossato.
The Monadic Way (2006)
[Link]
[Dau02] Hal Daume III.
Yet Another Haskell Tutorial (2002)
[Link]
[New] Jeff Newbern.
All about Monads
[Link]

75.31 Teora de Lenguajes 42 [Link] cuatrimestre 2006

También podría gustarte