Вы находитесь на странице: 1из 35

PARADIGMAS DE PROGRAMACION

Lambda Calculus Calculus Clculo Lambda


Referencia is!"rica
Moses Scofin#el (1920) Teora de funciones, combinadores.
$as#ell Curr% (1930) -calculus.
S!e&en 'leene Demuestra que el -calculus es un sistema computacional.
(on McCar!% (1950) nventa Lisp inspirado en el -calculus.
Pe!er Landin (1960) Demuestra que el significado de los programas imperativos puede
ser especificado trasladndolos al -calculus. nventa SWM lenguaje de programacin
prototipo.
Dana Sco!! inventa la teora de dominios.
Cris!o&er S!race% se bas en estos trabajos para sentar los fundamentos en el rea
de la semntica denotacional.
Pe!er $enderson y (im Morris (1970) Manifiestan la importancia de la programacin
funcional en la ingeniera del software.
Da)id *urner propuso que los combinadores podan ser usados como cdigo de mquina
de computadores para ejecutar programas funcionales. Tales computadores pueden
explotar las propiedades matemticas del -calculus para la evaluacin paralela de
programas.
Durante los aos 80 varios grupos trabajan sobre estas ideas para realizar programacin
funcional prctica diseando arquitecturas especiales para soportarlas.
Como podemos observar un brazo de la matemtica lgica trajo aparejado importantes
desarrollos en la teora de lenguajes de programacin:
Estudio de cuestiones fundamentales de computacin.
Diseo de lenguajes de programacin.
Semntica de lenguajes de programacin.
Arquitectura de computadoras.
Sin!+is % semn!ica del ,calculus
El -calculus es una notacin para la definicin de funciones.
Las expresiones de la notacin son llamadas -expresiones cada una de las cuales
denota una funcin.
Hat tres tipos de -expresiones:
) -ariables: x, y, z, etc. Las funciones pueden ser denotadas por variables. Se usan
V1, V2, para variables arbitrarias.
) A&licaci"n o combinaci"n de funciones: Si E1 y E2 son -expresiones entonces
(E1E2) denota el resultado de aplicar E1 a la funcin E2. E1 es llamado "rator
(operator) y E2 es llamado "rand (operand).
) Abs!racci"n: si V es una variable y E es una -expresin entonces V.E es una
abstraccin con la variable acotada V en el cuerpo E. Tal abstraccin denota la
funcin que toma un argumento a y retorna como resultado la funcin denotada por
E en un entorno en el cual la variable acotada V denota a.
Sintaxis de Expresiones usando BNF:
<-expresion> ::= <variable>
| ( <-expresion> <-expresion> )
| ( <variable> <-expresion> )
se puede simplificar a:
E ::= V | (E1 E2) | V.E
Alcance de )ariables % subs!i!uci"n
En (x.M) llamamos a x variable acotada y a M cuerpo. Toda ocurrencia de x en M est
acotada por la abstraccin. Una ocurrencia de una variable es libre si no est acotada por
alguna abstraccin que la encierre. Por ejemplo x est acotada e y es libre en (z.(x(yx)).
La abstraccin (x.M) representa la funcin ftal que f(x) = M para todo x.
(x.x) Denota la funcin identidad .
f(x) = x ((x.x)E) = E
(y.x) Denota una funcin constante.
f(y) = x
Con)enci"n no!acional
1) La aplicacin de funciones se asocia por la izquierda.
E1 E2 ... En significa ((... (E1 E2) ...) En)
E1 E2 ==> (E1 E2)
E1 E2 E3 ==> ((E1 E2) E3)
E1 E2 E3 E4 ==> (((E1 E2) E3) E4)
2) V.E1 E2 ... En significa (V.(E1 E2 ... En)) el alcance de V se extiende a la
derecha tan lejos como sea posible.
3) V1...Vn.E significa (V1.( ...(Vn.E) ...))
xy.E ==> (x.(y.E))
xyz.E ==> (x.(y.(z.E)))
Ejemplo: (x.(f.(fx)))
(x.(f.(fx))) ==> (f.(fx))) [E/x] ==> f.(fE)
(fE) [E'/f] ==> E' E
-ariables libres % aco!adas
(x.yx)(y.xy) en la primer expresin y es libre y x acotada mientras que en la segunda
expresion x es libre e y acotada.
BV(M) es el conjunto de todas las variables acotadas(bounded)en M.
BV(x) =
BV(x.M) = BV(M) U {x}
BV(MN) = BV(M) U BV(N)
FV(M) es el conjunto de todas las variables libres en M.
FV(x) =
FV(x.M) = FV(M) U {x}
F(MN) = F(M) U F(N)
M[L/y] denota el resultado de substituir L para todas las ocurrencias libres de y en M.
x[L/y] = L si x=y
x
(x.M)(L/y) = x.M si x=y
(x.M) [L/y]
(MN) [L/y] = {(M[L/y] N[L/y])}
E)i!ar la ca&!ura de )ariables en la subs!i!uci"n
La substitucin no debe perturbar los vnculos de variables. Consideremos el trmino (x.
(y.x)) cuando aplicamos un argumento N retorna la funcin constante (y.N). Esto no
funciona cuando N=y, tendremos (y.x)[y/x] = (y.y) se tranforma en la funcin identidad.
La ocurrencia libre de x cambia a una ocurrencia acotada de y un ejemplo de captura de
variable.
Si esto estuviese permitido el -calculus sera inconsistente. La substitucin M[N/x] es
segura previendo que las variables acotadas de y son disjuntas de las variables libres
de N.
BV(N) FV(N) =
Con)ersiones
La idea de que las -abstracciones representan funciones est formalmente expresada a
travs de las reglas de conversin para manipularlas.
Ejemplo: una expresin aritmtica como (2+3)*5 puede ser representada como una -
expresin y su valor 25 tambin puede ser representado como una -expresin. El
proceso de simplificar (2+3)*5 a 25 ser representado por un proceso de conversin o
reduccin. Las reglas de conversin son muy generales, cuando son aplicadas a -
expresiones representando expresiones aritmticas simulan la evaluacin aritmtica.
Estableciendo la notacin E[E'/V] en las reglas de conversin es empleada para significar
el resultado de subtituir E' por cada ocurrencia libre de V en E. Esta substitucin se dice
vlida si slo si ninguna variable libre de E' se torna cotada en E[E'/V].
Hay tres tipos de de -conversiones llamadas d-conversiones, -conversiones y q-
conversiones.
.,con)ersi"n: cualquier abstraccin de la forma V.E puede ser convertida a V'.E[V'/V]
provista la substitucin de V' para V en E sea vlida.
La d-conversin (x.M) (y.M [y/x]) renombra la variable acotada de la abstraccin de x
a y. Esto es vlido proveyendo que y no ocurra (libre o acotada) en M. por ejemplo (x.
(xz)) (y.(yz)).
Una -expresin (necesariamente una abstraccin) a la cual se le puede aplicar una d-
reduccin es denominada d-redex. El trmino d-redex abrevia la expresin "Reducible
Expression.
/,con)ersi"n: cualquier aplicacin de la forma (V.E1)E2 puede ser convertida a E1
[E2/V], previendo que la substitucin de E2 por V en E1 sea vlida.
La -conversin (x.M)N) M[N/x] substituye el argumento N en el cuerpo de la
abstraccin M. es vlida si BV(M) FU(N) = .
Por ejemplo: (x.(xx))(yz) ((yz)(yz))
(z.(zy))(x.x) (x.x)y y
Una -expresin (necesariamente una abstraccin) a la cual se le puede aplicar una -
reduccin es denominada -redex. La regla -conversin es como la evaluacin de un
llamado a funcin en un lenguaje de programacin. El cuerpo E1 de una funcin V.E1 es
evaluado en un entorno en el cual el parmetro formal V es acotado al parmetro actual.
0,con)ersiones1 cualquier abstraccin de la forma V.(EV) en la cual V no tiene
ocurrencia libre en E puede ser reducida de E. La q-conversin (x.(Mx)) M colapsa la
funcin trivial (x.(Mx)) en M. Es vlida si x FV(M). Por lo tanto M no depende x.
Observe que las funciones (x.(Mx)) y M siempre retornan la misma respuesta MN,
cuando se aplica a cualquier argumento N. La q-conversin abarca el principio de
extensionabilidad.
Una -expresin (necesariamente una abstraccin) a la cual se le puede aplicar una q-
reduccin es denominada q-redex. La regla q-conversin expresa la propiedad de que dos
funciones son iguales si dan el mismo resultado para iguales argumentos. Esta propiedad
es llamada Extensionabilidad.
Por ejemplo las q-converiones aseguran que x.(sinx) y sin denotan la misma funcin.
Ms generalmente (V(EV)) denota la funcin a la cual cuando se le aplica un argumento
E' retorna (EV)[E'/V]. Si V no ocurre libre en E entonces (EV)[E'/V] = (EE'). Por lo tanto
V.EV y E ambas conducen al mismo resultado EE', cuando son aplicadas a mismo
argumento y por lo tanto denotan la misma funcin.
El tipo ms importante de conversin es la -conversin, es usada para simular
mecanismos de evaluacin arbitrarios. La d-conversin esta hecha para la manipulacin
tcnic de variables acotadas, y las q-converiones expresan el hecho de que dos
funciones que entregan el mismo reusltado para iguales argumentos son iguales.
Ejemplos:
.,con)ersi"n x.x y.y
x.fx y.fy
No es el caso de x.y.add xy y.y.add xy
porque las substitucin y.add xy [y/x] no es vlida ya que el y que reemplaza x se est
acotado.
/,con)ersi"n (x.fx)E fE
(x.(y.add xy))3 y.add 3y
(y.add 3y))4 add 3 4
No es el caso de (x.(y.add xy)) (square y) y.add (square y)y
porque las substitucin y.add xy [(square y)/x] no es vlida ya que el y es libre en (square
y) pero se hace acotada en (y.add xy).
0,con)ersi"n x.add x add
y.add xy add x
No es el caso de x.add xx addx porque x es libre en add x.
Reducciones
M N o M reduce a N2 si M / N o M 0 N3
La reduccin M N puede consistir en aplicar una conversin a algn subtrmino de M
en orden a crear N. Ms formalmente podemos introducir la reglas de inferencia:
M M' M M' M M'
----------- ---------- -----------
(x.M) (x.M') (MN) (M'N) (LM) (LM')
Normali4aci"n
Si un trmino no admite reducciones entonces est en una forma normal. Normalizar un
trmino significa aplicar reducciones hasta llegar a una forma normal. Un trmino tiene
una forma normal si puede ser reducido a un trmino en forma normal.
Muchos -trminos no pueden ser reduciodos a la forma normal, se denominan C.
I5ualdad % normali4aci"n
El -calculus es una teora ecuacional, consiste de reglas para probar que dos trminos
son iguales. Una propiedad clave es que dos trminos son igaules si ambos pueden ser
reducidos al mismo trmino.
Reducci"n de m6l!i&les &aso
Estrictamente hablando M N significa que M reduca a N en un paso de reduccin,
posiblemente aplicado a un subtrmino de M. Frecuentemente, estamos interesados
cuando M puede ser reducido a N en algn nmero de pasos. M -->> N.
M M1 M2 ... Mk = N (k>= 0)
nformalmente M = M' si M puede ser trasformada en M' realizando cero o ms
reducciones y expansiones. Una expansin es la inversa de la reduccin.
La igualdad satisface todas las propiedades estandar. Primero es una relacin de
equivalencia, satisface las leyes reflexiva, simtrica y asociativa.
M = M M = N L = M M = N
------- ------------
N = M L = N
Tambin satisface las leyes de congruencia para cada modo de construccin de -
trminos.
La igauldad de -trminos es la menor relacin que satrisface las seis reglas dadas.
Es importante tener en claro la diferencia entre igualdad e idntico. Dos -expresiones son
idnticas si consisten exactamente de las mismas secuencias de caracteres; son iguales
si pueden ser convertidas una en otra.
La igualdad (7) est definida en trminos de identidad (8) y de conversiones (. , /,
0).
La relacion ,,9
Vimos que E1 = E2 puede ser definido para signifcar que E2 puede ser obtenida de E1
por una secuencia de conversiones o expansiones. Un caso especial es cuando E2 slo
puede obteerse de E1 usando solamente reducciones. Entonced E1 -- E2.
E+!ensionabilidad
Supuesto que V no ocurre libre en E1 o E2 y E1V = E2V
Por la ley de Leibnitz: V.E1V = V.E2V
aplicando q-reduccin en ambos lados E1 = E2, es a menudo conveniente probar que dos
-expresiones son iguales usando esta propiedad.
:unciones Curr%
El -calculus tiene solamente funciones de un argumento. Una funcin con argumentos
mltiples es expresada usando una funcin cuyo resultado es otra funcin.
Por ejemplo, suponemos que L es untrmino conteniendo solamente x e y como variables
libres, deseamos formalizar la funcin f(x,y) = L. La abstraccin (y.L) contiene a x libre.
Para cada x se establece una funcin sobre y. La abstraccin (x.(y.L)) no contiene
variables libres; cuando se aplica a los argumentos M y N, el resultado es obtenido
reemplazando x por M e y por N en L. Simblicamente realizamos -conversiones.
(x.(y.L))M)N) (y.L[M/x])N) L[M/x][N/y]
Esta tcnica es conocida como currying, y una funcin puede ser expresada usando 's
anidados. Trabaja para cualquier nmero de argumentos.
*eorema de Curc,Rosser
Este teorema fundamental establece que la reduccin en -calculus es confluente:
ninguna de dos secuencias de reducciones, comenzando de un -trmino pueden llegar a
formas normales distintas. La forma normal de un trmino es independiente del orden en
que se realizen las reducciones.
Si M = N y N est en forma normal entonces existe L tal que M -- L y N -- L.
El teorema tiene varias consecuencias importantes.
Si M = N y N est en forma normal, entonces M -- N.
Si un trmino puede ser transformado en su forma normal usando reducciones y
expansiones, entonces la forma normal puede ser alcanzada slo por reducciones.
Si M = N donde ambos trminos estn en forma normal entonces M = N (salvo renombrar
variables acotadas). Contrariamente si M y N estn en forma normal y son distintas
entonces M = N; no hay camino de transformacin de M en N.
Si E1 E2 entonces se puede decir que E2 se logra de E1 por evaluacin. Si no hay ( o
q)redex en E2 entonces se puede decir que est evaluada completamente. Una -
expresin se dice en forma normal si no contiene ( o q)redex, solamente se pueden
aplicar d-converisones. Por lo tanto una forma normal est evaluada completamente.
Los resultados de las reducciones no dependen del orden en que sean realizadas, redex
separadas pueden ser evaluadas en paralelo.
Pro&iedad diaman!e
M

M1 M2
L
La reduccin de un paso no satisface la propiedad diamante.
Posibilidad de no !erminaci"n
A pesar que diferentes secuencias de reduccin no pueden dar formas normales
diferentes, pueden llevar a resultados diferentes una puede no terminar nunca.
Tpicamente si M tiene una forma normal y admite una secuencia infinita de reduccin
contiene un subtrmino L que no tiene forma normal y L puede ser borrado por una
reduccin.
*eorema de normali4aci"n
Si E tiene una forma normal, entonces reduciendo repetidamente desde la izquierda ( o
q)redex (posiblemente despus de una d-conversin para evitar substituciones invlidas)
terminaremos en una expresin en forma normal.
Reducci"n normal de orden
La estrategia de reduccin a orden normal es en cada paso realizar la ms externa de la
izquierda -reduccin.
Las q-reducciones se dejan para el final. La ms izquierda significa reducir L antes que N
en LN. Ms externa significa reducir (x.M)N antes de reducir M por N.
La reduccin de orden normal corresponde a una evaluacin "call-by-name. Por el
teorema de estandarizacin siempre se llega a la forma normal si existe.
Sin embargo notamos que reduciendo L primero en LN puede transformar L en una
abstraccin (x.N). Reduciendo (x.M)N puede borrar N.
Es un problema difcil encontar un algoritmo ptimo para elegir el prximo redex a reducir.
Debido a que la reduccin de orden normal aparece como ineficiente, algunos lengiajes
basados en -calculus, han usado "call-by-value a pesar de que en algunos casos no
terminan nunca. Realmente, call-by-value tiene otras ventajas adems de eficiencia,
especialmente en lenguajes impuros (ejemplo: construidos con efecto lateral-
asignaciones). Por otro lado, investigaciones recientes sugieren que la evaluacin de
orden normal puede no ser tan ineficiente si se usan estrategias como la reduccin de
grafos.
;; *is im&lemen!a!ion su&&or! e)alua!ion of anon%mous recursion !rou5 <
Combina!or =e+3 < :ac!orial>
type exp = | Var of string
| Lambda of string * exp
| Apply of exp * exp
let rec subst x v a =
match a with
| Var y ->
if x = y then v else a
| Lambda(y, a') ->
if x = y then a else Lambda(y, subst x v a')
| Apply(a', a'') ->
Apply(subst x v a', subst x v a'')
let rec reduce e =
let rec reduce' e =
match e with
| Var _ -> e
| Lambda (s, e') -> Lambda(s, reduce' e')
| Apply(e1, e2) ->
match e1 with
| Lambda(s, e3) -> subst s e2 e3
| _ -> Apply(reduce' e1, reduce' e2)
reduce' e

let rec loop f x =
let x' = f x
if x = x' then x' else loop f x'
let normalOrderReducer = loop reduce
E)aluaci"n &ereso4a
Desde un punto de vista terico, la reduccin de orden normal es ptima, ya que siempre
llega a la forma normal si existe. Desafortunadamente ineficiente desde el punto de vista
computacional prctico (call-by-name). Call-by-value se podra evaluar de forma eficiente
pero podra no terminar.
La evaluacin perezosa o call-by-need, nunca evala un argumento ms que una sola
vez. Un argumento no es evaluado hasta que realmente sea requerido para producir la
respuesta; an as, el argumento es solamente evaluado a la necesidad de extensin (por
lo que permite listas infinitas). La evaluacin perezosa puede ser implementada
representando el trmino por un grafo ms que por un rbol. Cada nodo del grafo
comparido representa un subtrmino cuyo valor es necesitado ms de una vez. Cuando el
subtrmino es reducido, el resultado sobreescribe el nodo y las otras referencias tendrn
inmediatamente acceso al reemplazo.
La reduccin de grafos es ineficiente para el -calculus porque los subtrminos
tipicamente contienen variables libres. Durante cada -reduccin, el cuerpo de la
abstraccin debe ser apropiado.
Codificaci"n de da!os
A primera vista el -calculus aparece como un lenguaje muy primitivo. Sin embargo,
puede ser usado para representar la mayora de los objetos y estrucutras necesarias en la
programacin moderna.
La idea es codificar los objetos de manera tal que tengan las propiedades deseadas. Esta
codificacin nos permite virtualemte modelar la programacin funcional completa dentro
de los simple confines del -calculus.
Da!os boleanos
Una codificacin de boleanos debe definir los trminos true, false, if satisfaciendo (para
todo M N )
if true M N = M
if false M N = N
true = xy.x
false = xy.y
if = pxy.pxy
true MN = (xy.x) MN M
false MN = (xy.y) MN N
if LMN LMN es una funcin identidad en L.
if true MN M
if false MN N
Ne5aci"n
not true = false
not false = true
not = t.t false true
not true = (t.t false true) true
= true false true
= (xy.x) false true
= (y.false) true q
= false
Con?unci"n
or true true = true
or true false = true
or false true = true
or false false = false
or = pq.if p true q
Dis%unci"n
and true true = true
and true false = false
and false true = false
and false false = false
and = pq.if p q false
Pares ordenados
La funcin pair, que construye pares, y las proyecciones fst, snd, las que seleccionan lo
componentes del par estn codificadas de la siguiente manera:
pair = xyf.fxy
fst = p.p true
snd = p.p false
Una funcin pair puede ser aplicada a cualquier funcin de la forma xy.L, retornando
L[M/x][N/y] por lo tanto cada par es su propia opercain de empaquetado. La proyeccin
trabaja sobre la operacin de empaquetado:
fst (pair M N) fst(f.f M N) (f.f M N)true true M N M
similarmente snd(pair M N) N
Observe que los componentes del pair M N son completamente independientes; cada uno
puede ser extrado an si el otro no tiene forma normal.
Tfst = p.p truep true
= p.p true
fxy
*u&las
As como la funcin pair es una estructura de datos de dos componentes, la tupla es la
generalizacin para n componentes definidos en trminos de pares ordenados.
(E1,E2, ..., En) = (E1,(E2,( ... (En-1,En) ... )))
N6meros Na!urales
La siguiente codificacin de nmero naturales es la desarrollada originalmente por
Church:
0 = fx.x
1 = fx.fx
2 = fp.f(fx)
...
n = fx.f(...(fx) ...)
Para todo n>= 0 el numeral de Chirch n es la funcin que mapea f a fn. Cadanumeral es
un iterador (operador de iteracin).
Ari!m@!ica sobre n6meros de Curc
add = m n f x.m f (n f x)
add m n f x.m f (n f x) f x. f (f x) f x. f x) = m + n
mult = m n f x.m f (n f) x
mult m n f x.m f (n f) x f x. (nf )m x f x.( f )n* m x = m * n
exp = m n f x. n m f x
O&eraciones bsicas &ara n6meros Curc
Las operaciones definidas no son suficientes para definir todas las funciones compatibles
sobre nmeroc Church.
Sucesor suc = n f x. f( n f x )
Test de cero iscero = n.n ( x. false) true
suc n f x. f( n f x ) f x. f ( f^n x ) = f x. f ^n+1 x = n + 1
La funcin predecesor y substracin pueden ser codificadas como sigue:
prefn = fp.pair( f(dst p)) (fst p)
pre = nf x. snd(n(prefn f) (pair xx))

sub = mn. n pre m
La definicin de la funcin predecesor es dificultosa cuando cada numeral es un iterador.
Debemos reducir un iterador n + 1 a un iterador n. Dado f y x, debemos encontrar algn g
e y tal que g ^n+1 y compute f^n x. Una funcin g es una funcin sobre pares que mapea
(x,z) a (f(x),x) entonces:
g ^n+1(x,x) = (f^n+1(x), f^n(x))
El par acta como lnea de retardo de un elelemto y prefn f construye la funcin g.
La resta, sub m n computa el ensimo predecesor de m.
Tamben se puede puede aplicar a la suma:
add m n = mn.m suc n
add m n m suc n m (nfx.f(nfx)) n
m (fx.fnfx) (suc)^m n = m + n
Lis!as
Los nmeros de Church pueden ser generalizado para representar listas. La lista
[x1,x2,...,xn] puede ser escencialmente representada por la funcin que toma f e y para
fx1(fx2 ... fxn y). Tales listas pueden llevar su propia estructura de control en ellas.
nil = z.z
cons = xy. pair false (pair x y)
null = fst
hd = z. fst (snd z)
tl = z. snd (snd z)
:unciones recursi)as
La recursin general puede ser derivada en el -calculus. Por lo tanto podemos modelar
las definiciones de funciones recursivas, an cuando fallen en la terminacin para algunos
o todos los argumentos. La codificacin de la recursin es completamente uniforme e
independiente de los detalles de la definicin recursiva y de la representacin de las
estructuras de datos.
El secreto est en usar un combinador de punto fijo, un trmino Y tal que YF = F(Y F)
para todos los trminos F. Un punto fijo de la funcin F es cualquier X tal que Fx = X; aqu
X = YF.
Un combinador es un -trmino que no contiene variables libres. Para codificar la
recursin F representa el cuerpo de la definicin recursiva; la ley YF = F(YF) permite que
sea abarcada tantas veces como sea necesaria. Esto significa que YF no cambia cuando
la funcin F es aplicada a el.
En general si FF'=F', entonces F' es llamado un punto fijo de F.
Una -expresin Fix con la propiedad Fix E = E(Fix E) para cualquier E se denomina
operador de punto fijo.
El operador de punto fijo ms conocido es el combinador Y:
Y = f.(fx.f(xx))(x.f(xx))
YE = f.(fx.f(xx))(x.f(xx))E definicin de Y
= (x.E(xx))(x.E(xx)) -conversin
= E(x.E(xx))(x.E(xx)) -conversin
7 E =<E>
Consiste de dos -reducciones seguidas por una expansin. La reduccin no es posible
para YF -->> F(YF).
DefinicAon &or recursi"n
Para representar la multiplicacin en -calculus podemos definir:
mult m n = add n(add n(...(add n 0)...)
mult m n = (iscero m 0 | add n (mult((pre m)n))
mult = mn.(iscero m 0 | add n(mult(pre m)n)
Una ecuacin de la forma f x1 ... xn = E es llamada recursiva si f ocurre libre en E. El
combinador Y provee una forma general de resolver tales ecuaciones.
fact N = if (iscero N)(mult N (fact(pre N)))
fact = Y (gn.if (iscero n)(mult n(g(pre n)))
append z w = if (null z)w(cons(hd z)(append(tl z)w))
append = Y(gzw.if (null z)w(cons(hd z)(g(tl z)w))
En general la ecuacin de recursin M = PM, donde P es cualquier -trmino, es
satisfecha por M = YP.
Pro5ramaci"n :uncional
Modelos de com&u!aci"n diferen!e al modelo im&era!i)o !radicional3
En el modelo de la programacin funcional o aplicativa, los problemas se describen
mediante funciones matemticas puras sin efectos laterales, y en el modelo de la
programacin lgica o declarativa, los problemas se describen mediante relaciones entre
objetos.
Haskell es un lenguaje funcional puro, de propsito general, que incluye muchas de las
ltimas innovaciones en el desarrollo de los lenguajes de programacin funcional, como
son las funciones de orden superior, evaluacin perezosa, tipos polimrficos estticos,
tipos definidos por el usuario, encaje por patrones, y definiciones de listas por
comprehensin.
ncorpora, adems, otras caractersticas interesantes como el tratamiento sistemtico de
la sobrecarga, la facilidad en la definicin de tipos abstractos de datos, el sistema de
entrada/salida puramente funcional y la posibilidad de utilizacin de mdulos.
Problemas del Modelo im&era!i)o
Las asignaciones producen una serie de efectos laterales que oscurecen la semntica del
lenguaje.
Dificultan el estudio de la correccin de un programa e impiden que el compilador puede
realizar evaluaciones en paralelo. Adems, el compilador tiene graves problemas para
optimizar cdigo con asignaciones destructivas.
Modelo :uncional
El modelo funcional, tiene como objetivo la utilizacin de funciones matemticas puras sin
efectos laterales y, por tanto, sin asignaciones destructivas.
El valor que devuelve una funcin est nicamente determinado por el valor de sus
argumentos consiguiendo que una misma expresin tenga siempre el mismo valor (esta
propiedad se conoce como transparencia referencial). Es ms sencillo demostrar la
correccin de los programas ya que se cumplen propiedades matemticas tradicionales
como la propiedad commutativa, asociativa, etc.
El programador se encarga de definir un conjunto de funciones sin preocuparse de los
mtodos de evaluacin que posteriormente utilice el sistema.
La importancia de la programacin funcional no radica nicamente en no utilizar
asignaciones destructivas. Por el contrario, este modelo promueve la utilizacin de una
serie de caractersticas como las funciones de orden superior, los sistemas de inferencia
de tipos, el polimorfismo, la evaluacin perezosa, etc.
:unciones orden su&erior
Un lenguaje utiliza funciones de orden superior cuando permite que las funciones sean
tratadas como valores de primera clase, permitiendo que sean almacenadas en
estructuras de datos, que sean pasadas como argumentos de funciones y que sean
devueltas como resultados.
La utilizacin de funciones de orden superior proporciona una mayor flexibilidad al
programador, siendo una de las caractersticas ms sorbresalientes de los lenguajes
funcionales. De hecho, en algunos mbitos se considera que la propiedad que distingue
un lenguaje funcional de otro es la utilizacin de funciones de orden superior.
Sis!emas de Inferencia de *i&os % Polimorfismo
- El programador no est obligado a declarar el tipo de las expresiones
- El compilador contiene un algoritmo que infiere el tipo de las expresiones
- Si el programador declara el tipo de alguna expresin, el sistema chequea que el tipo
declarado coincide con el tipo inferido.
Los sistemas de inferencia de tipos permiten una mayor seguridad evitando errores de
tipo en tiempo de ejecucin y una mayor eficiencia, evitando realizar comprobaciones de
tipos en tiempo de ejecucin.
Por ejemplo, si el programador declara la siguiente funcin:
eligeSaludo x = if x then "adios"
else "hola"
El sistema infiere automticamente que el tipo es eligeSaludo::Bool -> String y, si el
programador hubiese declarado que tiene un tipo diferente, el sistema dara un error de
tipos.
Los sistemas de inferencia de tipos aumentan su flexibilidad mediante la utilizacin de
polimorfismo.
El polimorfismo permite que el tipo de una funcin dependa de un parmetro. Por ejemplo,
si se define una funcin que calcule la longitud de una lista, una posible definicin sera:
long ls = F vacia(L) then 0
else 1 + long(cola(L));
El sistema de inferencia de tipos inferira el tipo3: long::[x] -> nteger, indicando que tiene
como argumento una lista de elementos de un tipo a cualquiera y que devuelve un entero.
En un lenguaje sin polimorfismo sera necesario definir una funcin long para cada tipo de
lista que se necesitase. El polimorfismo permite una mayor reutilizacin de cdigo ya que
no es necesario repetir algoritmos para estructuras similares.
E)aluaci"n Pere4osa
Los lenguajes tradicionales, evalan todos los argumentos de una funcin antes de
conocer si stos sern utilizados. Por ejemplo:
g (x:nteger):nteger
Begin
(*Bucle infinito*)
while true do x:=x;
End;
f (x:nteger, y:nteger) :nteger
Begin
return (x+3);
End;
--- Programa Principal
BEGN
write(f(4,g(5)));
END.
Con el sistema de evaluacin tradicional, el programa anterior no devolvera nada, puesto
que al intentar evaluar g(5) el sistema entrara en un bucle infinito. Dicha tcnica de
evaluacin se conoce como evaluacin ansiosa (eager evaluation) porque evala todos
los argumentos de una funcin antes de conocer si son necesarios.
Por otra parte, en ciertos lenguajes funcionales se utiliza evaluacin perezosa (lazy
evaluation) que consiste en no evaluar un argumento hasta que no se necesita. En el
ejemplo anterior, si se utilizase evaluacin perezosa, el sistema escribira 7.
Uno de los beneficios de la evaluacin perezosa consiste en la posibilidad de manipular
estructuras de datos 'infinitas'. Evidentemente, no es posible construir o almacenar un
objeto infinito en su totalidad. Sin embargo, gracias a la evaluacin perezosa se puede
construir objetos potencialmente infinitos pieza a pieza segn las necesidades de
evaluacin.
E)oluci"n del modelo funcional
Los orgenes tericos del modelo funcional se remontan a los aos 30 en los cuales
Church propuso un nuevo modelo de estudio de la computabilidad mediante el clculo
lambda. Este modelo permita trabajar con funciones como objetos de primera clase. En
esa misma poca, Shnfinkel y Curry construan los fundamentos de la lgica
combinatoria que tendr gran importancia para la implementacin de los lenguajes
funcionales.
Hacia 1950, John McCarthy dise el lenguaje LSP (List Processing) que utilizaba las
listas como tipo bsico y admita funciones de orden superior.
En 1964, Peter Landin dise la mquina abstracta SECD para mecanizar la evaluacin
de expresiones.
En 1978 J. Backus (uno de los diseadores de FORTRAN y ALGOL) consigui que la
comunidad informtica prestara mayor atencin a la programacin funcional con su
artculo "Can Programming be liberated from the Von Neumann style? en el que criticaba
las bases de la programacin imperativa tradicional mostrando las ventajas del modelo
funcional.
A mediados de los 70, Gordon trabajaba en un sistema generador de demostraciones
denominado LCF que inclua el lenguaje de programacin ML (Metalenguaje).
SML (Stndar ML). Este lenguaje es fuertemente tipado con resolucin esttica de tipos,
definicin de funciones polimrficas y tipos abstractos.
Al mismo tiempo que se desarrollaban FP y ML, David Turner (primero en la Universidad
de St. Andrews y posteriormente en la Universidad de Kent) trabajaba en un nuevo estilo
de lenguajes funcionales con evaluacin perezosa y definicin de funciones mediante
encaje de patrones.
A comienzos de los ochenta surgieron una gran cantidad de lenguajes funcionales debido
a los avances en las tcnicas de implementacin.Esta gran cantidad de lenguajes
perjudicaba el desarrollo del paradigma funcional. En septiembre de 1987, se celebr la
conferencia FPCA en Portland, Oregon, en la que se discutieron los problemas que
creaba esta proliferacin. Se decidi formar un comit internacional que disease un
nuevo lenguaje puramente funcional de propsito general denominado Haskell.
Con el lenguaje Haskell se pretenda unificar las caractersticas ms importantes de los
lenguajes funcionales. como las funciones de orden superior, evaluacin perezosa,
inferencia esttica de tipos, tipos de datos definidos por el usuario, encaje de patrones y
listas por comprehensin. Al disear el lenguaje se observ que no exista un tratamiento
sistemtico de la sobrecarga con lo cual se construy una nueva solucin conocida como
las clases de tipos.
El lenguaje incorporaba, adems, Entrada/Salida puramente funcional y definicin de
arrays por comprehensin.
En Mayo de 1996 apareca la versin 1.3 del lenguaje Haskell [Has95] que incorporaba,
entre otras caractersticas, mnadas para Entrada/Salida, registros para nombrar
componentes de tipos de datos, clases de constructores de tipos y diversas libreras de
propsito general. Posteriormente, surge la versin 1.4 con ligeras modificaciones.
En 1998 se ha decidido proporcionar una versin estable del lenguaje, que se denominar
Haskell98 a la vez que se contina la investigacin de nuevas caractersticas.
Len5ua?e $as#ell
Conce&!os bsicos
El entorno HUGS funciona siguiendo el modelo de una calculadora en el que se establece
una sesin interactiva entre el ordenador y el usuario. Una vez arrancado, el sistema
muestra un prompt "?" y espera a que el usuario introduzca una expresin (denominada
expresin inicial y presione la tecla <RETURN>. Cuando la entrada se ha completado, el
sistema evala la expresin e imprime su valor antes de volver a mostrar el prompt para
esperar a que se introduzca la siguiente expresin.
Ejemplo:
? (2+3)*8
40
? sum [1..10]
55
En los ejemplos anteriores, se utilizaron funciones estndar, incluidas junto a una larga
coleccin de funciones en un fichero denominado "estndar prelude" que es cargado al
arrancar el sistema. Con dichas funciones se pueden realizar una gran cantidad de
operaciones tiles. Por otra parte, el usuario puede definir sus propias funciones y
almacenarlas en un fichero de forma que el sistema pueda utilizarlas en el proceso de
evaluacin.
Por ejemplo, el usuario podra crear un fichero fichero.hs con el contenido:
cuadrado::nteger -> nteger
cuadrado x = x * x
menor::(nteger, nteger) -> nteger
menor (x,y) = if x <= y then x else y
Para poder utilizar las definiciones anteriores, es necesario cargar las definiciones del
fichero en el sistema. La forma ms simple consiste en utilizar el comando ":load":
? :l fichero.hs
Reading script file "fichero.hs"
?
Si el fichero se carg con xito, el usuario ya podra utilizar la definicin:
12
? cuadrado (3+4)
49
? cuadrado (menor (3,4))
9
Es conveniente distinguir entre un valor como ente abstracto y su representacin, un
expresin formada por un conjunto de smbolos. En general a un mismo valor abstracto le
pueden corresponder diferentes representaciones. Por ejemplo, 7+7, cuadrado 7, 49,
XLX (49 en nmeros romanos), 110001 (en binario) representan el mismo valor.
El proceso de evaluacin consiste en tomar una expresin e ir transformndola aplicando
las definiciones de funciones (introducidas por el programador o predefinidas) hasta que
no pueda transformarse ms. La expresin resultante se denomina representacin
cannica y es mostrada al usuario.
Evaluar los argumentos antes de llamar a la funcin, se denomina llamada por valor y se
asocia con la evaluacin ansiosa. El segundo se denomina llamada por nombre y est
asociado a la evaluacin perezosa. En algunas ocasiones, la llamada por valor puede no
terminar, mientras que la llamada por nombre s. Por ejemplo, si se carga el siguiente
programa:
infinito:: nteger
infinito = infinito + 1
tres:: nteger -> nteger
tres x = 3
Al evaluar la expresin "tres infinito" por valor se obtiene:
tres infinito
= { utilizando la definicin infinito = infinito + 1}
tres (infinito + 1)
= { utilizando la definicin infinito = infinito + 1}
tres ((infinito + 1) + 1)
= ...
Si se hubiese evaluado por nombre se hubiese obtenido:
tres infinito
= { utilizando la definicin tres x = 3}
3
Existen valores que no tienen representacin cannica (por ejemplo, las funciones) o que
tienen una representacin cannica infinita (por ejemplo, el nmero pi ).
Por el contrario, otras expresiones, no representan ningn valor. Por ejemplo, la expresin
(1/0) o la expresin (infinito). Se dice que estas expresiones representan el valor
indefinido.
Nombres de funci"n1 Iden!ificadores % o&eradores
Existen dos formas de nombrar una funcin, mediante un identificador (por ejemplo, sum,
product y fact) y mediante un smbolo de operador (por ejemplo, * y +) El sistema
distingue entre los dos tipos segn la forma en que estn escritos:
A. Un identificador comienza con una letra del alfabeto seguida, opcionalmente, por una
secuencia de caracteres, cada uno de los cuales es, una letra, un dgito, un apstrofe (') o
un subrayado (_).
Los identificadores que representan funciones o variables deben comenzar por letra
minscula (los identificadores que comienzan con letra mayscula se emplearn como
funciones constructoras).
Los siguientes son ejemplos de posibles identificadores:
sum f f'' intSum elemento_dos do'until'zero
Los siguientes identificadores son palabras reservadas y no pueden utilizarse como
nombres de funciones o variables:
case of where let in if then else data type infix infixl infixr primitive class instance
B. Un smbolo de operador es escrito utilizando uno o ms de los siguientes caracteres:
: ! # $ % & * + . / < = > ? @ \ ^ | -
Adems, el carcter (~) tambin se permite, aunque slo en la primera posicin del
nombre.
Los nombres de operador que comienzan con (:) son utilizados para funciones
constructoras como los identificadores que comienzan por mayscula mencionados
anteriormente. Los siguientes smbolos tienen usos especiales:
:: = .. @ \ | <- -> ~ =>
Todos los otros smbolos de operador se pueden utilizar como variables o nombre de
funcin, incluyendo los siguientes:
+ ++ && || <= == /= // .
==> $ @@ -*- \/ /\ ... ?
Se proporcionan dos mecanismos simples para utilizar un identificador como un smbolo
de operador o un smbolo de operador como un identificador:
C. Cualquier identificador ser tratado como un smbolo de operador si est encerrado
entre comillas inversas (`). As, cualquier expresin de la forma "x `id` y" es equivalente a
"id x y"
D. Cualquier smbolo de operador puede ser tratado como un identificador encerrndolo
en parntesis. Por ejemplo, "x + y" podra escribirse como "(+) x y".
Cuando se trabajan con smbolos de operador es necesario tener en cuenta:
A. La precedencia La expresin "2 * 3 + 4" podra interpretarse como "(2 * 3) + 4" o como
"2 * (3 + 4)". Para resolver la ambigedad, cada operador tiene asignado un valor de
precedencia (un entero entre 0 y 9). En una situacin como la anterior, se comparan los
valores de precedencia y se utiliza primero el operador con mayor precedencia (en el
standar prelude el (+) y el (*) tienen asignados 6 y 7, respectivamente, por lo cual se
realizara primero la multiplicacin).
B. La asociatividad: La regla anterior resolva ambigedades cuando los smbolos de
operador tienen distintos valores de precedencia, sin embargo, la expresin "1 - 2 3"
puede ser tratada como "(1 - 2) - 3" resultando -4 o como "1 - (2 3)" resultando 2. Para
resolverlo, a cada operador se le puede definir una regla de asociatividad. Por ejemplo, el
smbolo (-) se puede decir que es:
Asociativo a la izquierda: si la expresin "x-y-z" se toma como "(x-y)-z"
Asociativo a la derecha: si la expresin "x-y-z" se toma como "x-(y-z)"
No asociativo: Si la expresin "x-y-z" se rechaza como un error sintctico.
En el standar prelude el (-) se toma como asociativo a la izquierda, por lo que la expresin
"1 - 2 - 3" se tratar como "(1-2)-3".
Por defecto, todo smbolo de operador se toma como no-asociativo y con precedencia 9.
Estos valores pueden ser modificados mediante una declaracin con los siguientes
formatos:
infixl digito ops Para declarar operadores asociativos a la izquierda
infixr digito ops Para declarar operadores asociativos a la derecha
infix digito ops Para declarar operadores no asociativos ops representa una lista de uno o
ms smbolos de operador separados por comas y digito es un entero entre 0 y 9 que
asigna una precedencia a cada uno de los operadores de la lista. Si el dgito de
precedencia se omite se toma 9 por defecto.
La aplicacin de funciones tiene ms precedencia que cualquier smbolo de operador. Por
ejemplo, la expresin "f x + g y equivale a "(f x) + (g y)
Otro ejemplo que a menudo da problemas es la expresin "f x + 1", que es tratada
como "(f x)+1" en lugar de "f(x+1)".
*i&os
Una parte importante del lenguaje Haskell lo forma el sistema de tipos que es utilizado
para detectar errores en expresiones y definiciones de funcin.
El universo de valores es particionado en colecciones organizadas, denominadas tipos.
Cada tipo tiene asociadas un conjunto de operaciones que no tienen significado para
otros tipos, por ejemplo, se puede aplicar la funcin (+) entre enteros pero no entre
caracteres o funciones.
Una propiedad importante del Haskell es que es posible asociar un nico tipo a toda
expresin bien formada. Esta propiedad hace que el Haskell sea un lenguaje fuertemente
tipado. Como consecuencia, cualquier expresin a la que no se le pueda asociar un tipo
es rechazada como incorrecta antes de la evaluacin.
El anlisis del cdigo puede dividirse en dos fases: Anlisis sintctico, para chequear la
correccin sintctica de las expresiones y anlisis de tipo, para chequear que todas las
expresiones tienen un tipo correcto.
Informaci"n de !i&o
Adems de las definiciones de funcin, en el cdigo se puede incluir informacin de tipo
mediante una expresin de la forma A::B para indicar al sistema que A es de tipo B. Por
ejemplo:
cuadrado:: nt -> nt
cuadrado x = x * x
La primera lnea indica que la funcin cuadrado es del tipo "funcin que toma un entero y
devuelve un entero".
Aunque no sea obligatorio incluir la informacin de tipo, s es una buena prctica, ya que
el Haskell chequea que el tipo declarado coincide que el tipo inferido por el sistema a
partir de la definicin, permitiendo detectar errores de tipos.
*i&os &redefinidos
Los principales tipos predefinidos del sistema Haskell, stos se podran clasificar en: tipos
bsicos, cuyos valores se toman como primitivos, por ejemplo, Enteros, Flotantes,
Caracterses y Booleanos; y tipos compuestos, cuyos valores se construyen utilizando
otros tipos, por ejemplo, listas, funciones y tuplas.
Booleanos
Se representan por el tipo "Bool" y contienen dos valores: "True" y "False". El
standar prelude incluye varias funciones para manipular valores booleanos: (&&),
(||) y not.
x && y es True si y slo si x e y son True
x || y es True si y slo si x y ambos son True
not x es el valor opuesto de x (not True = False,not False = True)
Tambin se incluye una forma especial de expresin condicional que permite
seleccionar entre dos alternativas dependiendo de un valor booleano:
if exp_b then x else y
En!eros
Representados por el tipo "nt", se incluyen los enteros positivos y negativos tales como el
-273, el 0 el 383. Como en muchos sistemas, el rango de los enteros utilizables est
restringido. Tambin se puede utilizar el tipo nteger que denota enteros sin lmites
superior ni inferior.
En el standar prelude se incluye un amplio conjunto de operadores y funciones que
manipulan enteros:
(+) suma. (*) multiplicacin. (-) substraccin. (^) potenciacin.
negate menos unario (la expresin "-x" se toma como "negate x")
div divisin entera " " rem resto de la divisin entera.
Siguiendo la ley:
(x `div` y)*y + (x `rem` y) == x
mod mdulo, como rem slo que el resultado tiene el mismo signo que el divisor.
odd devuelve True si el argumento es impar
even devuelve True si el argumento es par.
gcd mximo comn divisor. lcm mnimo comn mltiplo.
abs valor absoluto
signum devuelve -1, 0 o 1 si el argumento es negativo, cero positivo, respectivamente.
:lo!an!es
Representados por el tipo "Float", los elementos de este tipo pueden ser utilizados para
representar fraccionarios as como cantidades muy largas o muy pequeas. Sin embargo,
tales valores son slo aproximaciones a un nmero fijo de dgitos y pueden aparecer
errores de redondeo en algunos clculos que empleen operaciones en punto flotante. Un
valor numrico se toma como un flotante cuando incluye un punto en su representacin o
cuando es demasiado grande para ser representado por un entero.
Tambin se puede utilizar notacin cientfica; por ejemplo 1.0e3 equivale a 1000.0,
mientras que 5.0e-2 equivale a 0.05.
El standar prelude incluye tambin mltiples funciones de manipulacin de flotantes:
pi, exp, log, sqrt, sin, cos, tan, asin, acos, atan, etc.
Carac!eres
Representados por el tipo "Char", los elementos de este tipo representan caracteres
individuales como los que se pueden introducir por teclado. Los valores de tipo caracter
se escriben encerrando el valor entre comillas simples, por ejemplo 'a', '0', '.' y 'Z'. Algunos
caracteres especiales deben ser introducidos utilizando
un cdigo de escape; cada uno de stos comienza con el caracter de barra invertida (\) ,
seguido de uno o ms caracteres que seleccionan el caracter requerido.
Algunos de los ms comunes cdigos de escape son:
'\\' barra invertida '\'' comilla simple '\"' comilla doble '\n' salto de lnea '\b' or '\BS'
backspace (espacio atrs)
'\DEL' borrado '\t' or '\HT' tabulador '\a' or '\BEL' alarma (campana) '\f' or '\FF' alimentacin
de papel
En contraste con algunos lenguajes comunes (como el C, por ejemplo), los valores del
tipo Char son completamente distintos de los enteros.
:unciones
Si a y b son dos tipos, entonces a->b es el tipo de una funcin que toma como argumento
un elemento de tipo a y devuelve un valor de tipo b.
Las funciones en Haskell son objetos de primera clase. Pueden ser argumentos o
resultados de otras funciones o ser componentes de estructuras de datos. Esto permite
simular mediante funciones de un nico argumento, funciones con mltiples argumentos.
Considrese, por ejemplo, la funcin de suma (+). En matemticas se toma la suma como
una funcin que toma una pareja de enteros y devuelve un entero. Sin embargo, en
Haskell, la funcin suma tiene el tipo:
(+)::nt->(nt->nt)6
(+) es una funcin de un argumento de tipo nt que devuelve una funcin de tipo nt->nt.
De hecho "(+) 5" denota una funcin que toma un entero y devuelve dicho entero ms 5.
Este proceso se denomina currificacin7 y permite reducir el nmero de parntesis
necesarios para escribir expresiones. De hecho, no es necesario escribir f(x) para denotar
la aplicacin del argumento x a la funcin x, sino simplemente f x.
Lis!as
Si a es un tipo cualquiera, entonces [a] representa el tipo de listas cuyos elementos son
valores de tipo a.
Hay varias formas de escribir expresiones de listas:
La forma ms simple es la lista vaca, representada mediante [].
Se podra escribir simplemente (+)::nt->nt->nt, puesto que el operador -> es
asociativo a la derecha.
- Las listas no vacas pueden ser construidas enunciando explcitamente sus elementos
(por ejemplo, [1,3,10]) o aadiendo un elemento al principio de otra lista
utilizando el operador de construccin (:). Estas notaciones son equivalentes:
[1,3,10] = 1:[3,10] = 1:(3:[10]) = 1:(3:(10:[]))
El operador (:) es asociativo a la derecha, de forma que 1:3:10:[] equivale a (1:(3:(10:[]))),
una listaa cuyo primer elemento es 1, el segundo 3 y el ltimo 10.
El standar prelude incluye un amplio conjunto de funciones de manejo de listas, por
ejemplo:
length xs devuelve el nmero de elementos de xs xs ++ ys devuelve la lista resultante de
concatenar xs e ys
concat xss devuelve la lista resultante de concatenar las listas de xss map f xs devuelve la
lista de valores obtenidos al aplicar la funcin f a cada uno de los elementos de la lista xs.
Todos los elementos de una lista deben ser del mismo tipo. La expresin ['a',2,False] no
est permitida en Haskell.
Cadenas
Una cadena es tratada como una lista de caracteres y el tipo "String" se toma
como una abreviacin de "[Char]". Las cadenas pueden ser escritas como
secuencias de caracteres encerradas entre comillas dobles. Todos los cdigos de
escape utilizados para los caracteres, pueden utilizarse para las cadenas.
? "hola"
hola
?
Adems, las cadenas pueden contener la secuencia de escape "\&" que puede ser
utilizada para separar pares de caracteres dentro una cadena, por ejemplo:
"\123h" representa la cadena ['\123', 'h']
"\12\&3h" representa la cadena ['\12', '3', 'h']
Puesto que las cadenas son representadas como listas de caracteres, todas las
funciones del standar prelude para listas pueden ser utilizadas tambin con cadenas:
? length "Hola"
*u&las
Si t1, t2, ..., tn son tipos y n>=2, entonces hay un tipo de n-tuplas escrito (t1, t2, ..., tn)
cuyos elementos pueden ser escritos tambin como (x1, x2,
..., xn) donde cada x1, x2, ..., xn tiene tipos t1,t2, ..., tn
respectivamente.
Ejemplo: (1, [2], 3) :: (nt, [nt], nt)
('a', False) :: (Char, Bool)
((1,2),(3,4)) :: ((nt, nt), (nt, nt))
Obsrvese que, a diferencia de las listas, los elementos de una tupla pueden tener tipos
diferentes. Sin embargo, el tamao de una tupla es fijo.
En determinadas aplicaciones es til trabajar con una tupla especial con 0 elementos
denominada tipo unidad. El tipo unidad se escribe como () y tiene un nico elemento que
es tambin ().
Definiciones de funci"n
Enca?e de &a!rones sim&le
La declaracin de una funcin f est formada por un conjunto de ecuaciones con el
formato:
f <pat1> <pat2> . . . <patn> = <expresion>8
Donde cada una de las expresiones <pat1> <pat2> . . . <patn> representa un argumento
de la funcin y se denominado un patrn. El nmero n de argumentos se denomina
aridad. Si f fuese definida por ms de una ecuacin, entonces stas deben definirse juntas
y cada una debe tener la misma aridad.
Cuando una funcin est definida mediante ms de una ecuacin, ser necesario evaluar
una o ms argumentos de la funcin para determinar cul de las ecuaciones aplicar. Este
proceso se denomina encaje de patrones. En los ejemplos anteriores se utiliz el patrn
ms simple: una variable. Como ejemplo, considrese la definicin de factorial:
fact n = product [1..n]
En los patrones se pueden utilizar constantes, como en la siguiente definicin de la
funcin "not" tomada del standar prelude:
not True = False
not False = True
Para determinar el valor de una expresin de la forma "not b", se debe evaluar antes la
expresin "b". Si el resultado es "True" entonces se utiliza la primera ecuacin y el valor
de "not b" ser "False". Si el valor de "b" es "False", entonces se utiliza la segunda
ecuacin y "not b" ser "True".
Otros tipos de patrones tiles seran:
Annimos: Se representan por el caracter (_) y encajan con cualquier valor, pero no es
posible referirse posteriormente a dicho valor. Ejemplo:
cabeza (x:_) = x
cola (_:xs) = xs
Patrones con nombre: Para poder referirnos al valor que est encajando, por ejemplo, en
lugar de definir f como:
f (x:xs) = x:x:xs
Podra darse un nombre a x:xs mediante un patrn con nombre:
f p@(x:xs) = x:p
8Si f fuese un operador sera <pat1> f <pat2> = <expresion>
Patrones n+k: Encajan con un valor entero mayor o igual que k. El valor referido por la
variable n es el valor encajado menos k. Ejemplo:
x ^ 0 = 1
x ^ (n+1) = x * (x ^ n)
En Haskell, el nombre de una variable no puede utilizarse ms de una vez en la parte
izquierda de cada ecuacin en una definicin de funci. As, el siguiente ejemplo:
son_iguales x x = True
son_iguales x y = False
no ser aceptado por el sistema. Podra ser introducido mediante if:
son_iguales x y = if x==y then True else False
Ecuaciones con 5uardas
Cada una de las ecuaciones de una definicin de funcin podra contener guardas que
requieren que se cumplan ciertas condiciones sobre los valores de los argumentos.
minimo x y | x <= y = x
| otherwise = y
En general una ecuacin con guardas toma la forma:
f x1 x2 ... xn | condicion1 = e1
| condicion2 = e2
| condicionm = em
Esta ecuacin se utiliza para evaluar cada una de las condiciones por orden hasta que
alguna de ellas sea "True", en cuyo caso, el valor de la funcin vendr dado por la
expresin correspondiente en la parte derecha del signo "=". En Haskell, la variable
"otherwise" evala a "True". Por lo cual, escribir "otherwise" como una condicin significa
que la expresin correspondiente ser siempre utilizada si no se cumpli ninguna
condicin previa.
Definiciones locales
Las definiciones de funcin podran incluir definiciones locales para variables que podran
en guardas o en la parte derecha de una ecuacin.
Considrese la siguiente funcin que calcula el nmero de races diferentes de una
ecuacin cuadrtica de la forma ax2 +bx +c = 0
numeroDeRaices a b c | discr>0 = 2
| discr==0 = 1
| discr<0 = 0
where discr = b*b 4*a*c
Las definiciones locales pueden tambin ser introducidas en un punto arbitrario de una
expresin utilizando una expresin de la forma:
let <decls> in <expr>
Por ejemplo:
? let x = 1 + 4 in x*x + 3*x + 1
41
No!aciones es&eciales
Secuencias ari!m@!icas
La notacin de las secuencias aritmticas permite generar una gran cantidad de listas
tiles.
Existen cuatro formas de expresar secuencias aritmticas:
[m..] Produce una lista (potencialmente infinita) de valores que comienzan con m y se
incrementan en pasos simples.
Ejemplo:
[1..] = [1, 2, 3, 4, 5, 6, 7, 8, 9, etc...
[m..n] Produce la lista de elementos desde m hasta n, con incrementos en pasos
simples. Si m es menor que n devuelve la lista vaca.
[m,m'..] Produce la lista (potencialmente infinita) de valores cuyos dos primeros elementos
son m y m'. Si m es menor que m' entonces los siguientes elementos de la lista se
incrementan en los mismos pasos. Si m es mayor que m' entonces los incrementos sern
negativos. Si son iguales se obtendr una lista infinita cuyos
elementos sern todos iguales.
Ejemplos:
[1,3..] = [1, 3, 5, 7, 9, 11, 13, etc...
[5,4..] = [5, 4, 3, 2, 1, 0, -1, etc...
Las secuencias no estn nicamente restringidas a enteros, pueden emplearse con
elementos enumerados9 como caracteres, flotantes, etc.
Ejemplo:
? ['0'..'9'] ++ ['A'..'Z']
0123456789ABCDEFGHJKLMNOPQRSTUVWXYZ
? [1.2, 1.35 .. 2.00]
[1.2, 1.35, 1.5, 1.65, 1.8, 1.95]
Lis!as &or com&reensi"n
La notacin de listas por comprehensin permite declarar de forma concisa una gran
cantidad de iteraciones sobre listas. Esta notacin est adaptada de la teora de conjuntos
de Zermelo-Fraenkel10. Sin embargo en Haskell se trabaja con listas, no con conjuntos.
El formato bsico de la definicin de una lista por comprehensin es:
[ <expr> | <cualif_1>, <cualif_2> . . . <cualif_n> ]
Cada <cualif_i> es un cualificador. Existen dos tipos:
Generadores: Un cualificador de la forma pat<-exp es utilizado para extraer cada
elemento que encaje con el patrn pat de la lista exp en el orden en que aparecen los
elementos de la lista. Un ejemplo simple sera la expresin:
? [x*x | x <- [1..10]]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Filtros: Una expresin de valor booleano, el significado de una lista por comprehensin
con un nico filtro podra definirse como:
[e |condicion ] = if condition then [e] else []
E+&resiones lambda
Adems de las definiciones de funcin con nombre, es posible definir y utilizar funciones
sin necesidad de darles un nombre explcitamente mediante expresiones lambda de la
forma:
\ <patrones atmicos> -> <expr>11
Esta expresin denota una funcin que toma un nmero de parmetros (uno por cada
patrn) produciendo el resultado especificado por la expresin <expr>. Por ejemplo, la
expresin:
(\x->x*x)
representa la funcin que toma un nico argumento entero 'x' y produce el cuadrado de
ese nmero como resultado.
E+&resiones case
Una expresin case puede ser utilizada para evaluar una expresin y, dependiendo del
resultado, devolver uno de los posibles valores.
paridad x = case (x mod 2) of
0 -> "par"
1 -> "impar"
Combinando expresiones case con las expresiones lambda de la seccin anterior, se
puede traducir cualquier declaracin de funcin en una simple ecuacin de la forma
<nombreFuncin> = <expr>.
Por ejemplo, la funcin stndar map, cuya definicin se escribe normalmente como:
map f [] = []
map f (x:xs) = f x : map f xs
podra tambin definirse mediante una nica ecuacin:
map = \f xs -> case xs of
[] -> []
(y:ys) -> f y : map f ys
Secciones
Esta es una generalizacin de las expresiones utilizadas en el clculo de funciones
lambda de Church. El
caracter `\' se ha elegido por su parecido con la letra griega lambda 'l '
La mayora de las funciones de ms de un argumento son tratadas como funciones de un
nico argumento, cuyo resultado es una funcin que puede aplicarse al resto de
argumentos. De esa forma, "(+) 5" denota una funcin que toma un argumento entero "n"
y devuelve el valor entero "5+n". Las funciones de este tipo son tan utilizadas que el
sistema proporciona una notacin especial para ellas. Si "e" es una expresin atmica y
"*" es un operador infijo, entonces tambin son funciones (e *) y (* e) conocidas
como "secciones del operador *". Su definicin es:
(e *) x = e * x
(* e) x = x * e
Las expressiones "(e *)" y "(* e)" son tratadas como abreviaciones de "(*) e" y "flip (*) e"
respectivamente, donde "flip" se define como:
flip :: (a -> b -> c) -> b -> a -> c
flip f x y = f y x
Dis&osici"n del c"di5o
Haskell utiliza una sintaxis bidimensional denominada espaciado (layout) que se basa
esencialmente en que las declaraciones estn alineadas por columnas. Las reglas del
espaciado son bastante intuitivas y podran resumirse en:
1.- El siguiente caracter de cualquiera de las palabras clave where, let, o of es el que
determina la columna de comienzo de declaraciones en las expresiones where, let, o case
correspondientes. Por tanto podemos comenzar las declaraciones en la misma lnea que
la palabra clave, en la siguiente o siguientes.
2.- Es necesario asegurarse que la columna de comienzo dentro de una declaracin est
ms a la derecha que la columna de comienzo de la siguiente clusula. En caso contrario,
habra ambigedad, ya que el final de una declaracin ocurre cuando se encuentra algo a
la izquierda de la columna de comienzo.
El espaciado es una forma sencilla de agrupamiento que puede resultar bastante til. Por
ejemplo, la declaracin anterior sera equivalente a:
ejemplo x y z = a + b
where { a = f x y ;
b = g z }
*i&os definidos &or el usuario
Sin"nimos de !i&o
Los sinnimos de tipo se utilizan para proporcionar abreviaciones para expresiones de
tipo aumentando la legibilidad de los programas. Un sinnimo de tipo es introducido con
una declaracin de la forma:
type Nombre a1 ... an = expresion_Tipo donde
- Nombre es el nombre de un nuevo constructor de tipo de aridad n>=0
- a1,..., an son variables de tipo diferentes que representan los argumentos de
Nombre
- expresion_Tipo es una expresin de tipo que slo utiliza como variables de tipo
las variables a1,..., an.
Ejemplo:
type Nombre = String
type Edad = nteger
type String = [Char]
type Persona = (Nombre, Edad)
tocayos::Persona -> Persona -> Bool
tocayos (nombre,_) (nombre',_) = n == nombre'
Definiciones de !i&os de da!os
A parte del amplio rango de tipos predefinidos, en Haskell tambin se permite definir
nuevos tipos de datos mediante la sentencia data. La definicin de nuevos tipos de datos
aumenta la seguridad de los programas ya que el sistema de inferencia de tipos distingue
entre los tipos definidos por el usuario y los tipos predefinidos.
*i&os Produc!o
Se utilizan para construir un nuevo tipo de datos formado a partir de otros. Ejemplo:
data Persona = Pers Nombre Edad
juan::Persona
juan = Pers "Juan Lopez" 23
Se pueden definir funciones que manejen dichos tipos de datos:
esJoven:: Persona -> Bool
esJoven (Pers _ edad) = edad < 25
verPersona::Persona -> String
verPersona (Pers nombre edad) = "Persona, nombre " ++ nombre ++ ", edad: " ++
show edad
Tambin se puden dar nombres a los campos de un tipo de datos producto:
data = Datos { nombre::Nombre, dni::nteger, edad:Edad }
Los nombres de dichos campos sirven como funciones selectoras del valor
correspondiente.
Por ejemplo:
tocayos:: Persona -> Persona -> Bool
tocayos p p' = nombre p == nombre p'
Obsrvese la diferencia de las tres definiciones de Persona
1.- Como sinnimo de tipos:
type Persona = (Nombre, Edad)
No es un nuevo tipo de datos. En realidad, si se define
type Direccion = (Nombre, Numero)
type Numero = nteger
El sistema no dara error al aplicar una funcin que requiera un valor de tipo persona con
un valor de tipo Direccin. La nica ventaja (discutible) de la utilizacin de sinnimos de
tipos de datos podra ser una mayor eficiencia (la definicin de un nuevo tipo de datos
puede requerir un mayor consumo de recursos.
2.- Como Tipo de Datos
data Persona = Pers Nombre Edad
El valor de tipo Persona es un nuevo tipo de datos y, si se define:
type Direccion = Dir Nombre Numero
El sistema dara error al utilizar una direccin en lugar de una persona. Sin embargo, en la
definicin de una funcin por encaje de patrones, es necesario conocer el nmero de
campos que definen una persona, por ejemplo:
esJoven (Pers _ edad) = edad < 25
Si se desea ampliar el valor persona aadiendo, por ejemplo, el "dni", todas las
definiciones que trabajen con datos de tipo Persona deberan modificarse.
3.- Mediante campos con nombre:
data Persona = Pers { nombre::Nombre, edad::Edad }
El campo s es un nuevo tipo de datos y ahora no es necesario modificar las funciones
que trabajen con personas si se amplan los campos.
*i&os Enumerados
Se puede introducir un nuevo tipo de datos enumerando los posibles valores.
Ejemplos:
data Color = Rojo | Verde | Azul
data Temperatura = Frio | Caliente
data Estacion = Primavera | Verano | Otonio | nvierno
Se pueden definir funciones simples mediante encaje de patrones:
tiempo :: Estacion -> Temperatura
tiempo Primavera = Caliente
tiempo Verano = Caliente
tiempo _ = Frio
*i&os Recursi)os
Los tipos de datos pueden autorreferenciarse consiguiendo valores recursivos, por
ejemplo:
data Expr = Lit nteger
| Suma Expr Expr
| Resta Expr Expr
eval (Lit n) = n
eval (Suma e1 e2) = eval e1 + eval e2
eval (Resta e1 e2) = eval e1 * eval e2
Tambin pueden definirse tipos de datos polimrficos. El siguiente ejemplo define un tipo
que representa rboles binarios:
data Arbol a = Hoja a | Rama (Arbol a) (Arbol a)
A continuacin se muestra una funcin que calcula la lista de nodos hoja de un rbol
binario:
hojas :: Arbol a -> [a]
hojas (Hoja h) = [h]
hojas (Rama izq der) = hojas izq ++ hojas der
Nue)os *i&os de Da!os a &ar!ir de !i&os e+is!en!es
En ciertas ocasiones puede desearse utilizar un tipo de datos ya existente (por motivos de
eficiencia) pero etiquetarlo para que distinguirlo del tipo de datos original12. Para ello, se
puede emplear la declaracin newtype.
Recurdese que los sinnimos de tipos no distinguen entre el tipo original y el sinnimo.
newtype Persona = Per (Nombre, Edad)
esJoven (Per (_, edad)) = edad < 25
nternamente, el tipo persona se representa como una tupla con dos valores, pero un
valor de tipo Persona se distingue mediante la etiqueta "Per".
En!rada;Salida
Hasta ahora, todas las funciones descritas tomaban sus argumentos y devolvan un valor
sin interactuar con el exterior. A la hora de realizar programas "reales" es necesario que
stos sean capaces de almacenar resultados y leer datos de ficheros, realizar preguntas y
obtener respuestas del usuario, etc.
Una de las principales ventajas del lenguaje Haskell es que permite realizar las tareas de
Entrada/Salida de una forma puramente funcional, manteniendo la transparencia
referencial y sin efectos laterales.
Para ello, a partir de la versin 1.3 se utiliza una mnada de Entrada/Salida.
Una expresin de tipo O a denota una computacin que puede realizar operaciones de
Entrada/Salida y devolver un resultado de tipo a.
A continuacin se declara una sencilla funcin que muestra por pantalla la cadena "Hola
Mundo":
main::O()
main = print "Hola, mundo!"
La funcin main tiene tipo O () indicando que realiza Entrada/Salida y no devuelve ningn
valor. Esta funcin tiene un significado especial cuando el lenguaje es compilado, puesto
que es la primera funcin evaluada por el sistema. En esta ocasin, se utiliza la funcin
print declarada en el Standar prelude que se encargar de imprimir su argumento en la
salida estndar.
:unciones bsicas de En!rada;Salida
A continuacin se muestran algunas de las funciones bsicas de Entrada/salida
predefinidas:
putChar::Char->O () mprime un caracter
getChar::O Char Lee un caracter
putStr::String->O () mprime una cadena
putStrLn::String->O () mprime una cadena y un salto de lnea
print::Show a => a ->O () mprime un valor de cualquier tipo imprimible (perteneciente a la
clase Show)
getLine::O String Lee una cadena de caracteres hasta que encuentra el salto de lnea
getContents::O String Lee en una cadena toda la entrada del usuario (esta cadena ser
potencialmente infinita y se podr procesar gracias a la evaluacin perezosa)
interact::(String->String)->O()
Toma como argumento una funcin que procesa una cadena y devuelve otra cadena. A
dicha funcin se le pasa la entrada del usuario como argumento y el resultado devuelto se
imprime.
writeFile::String->String->O()
Toma como argumentos el nombre de un fichero y una cadena; escribe dicha cadena en
el fichero correspondiente.
appendFile::String->String->O()
Toma como argumentos el nombre de un fichero y una cadena; aade dicha cadena al
final del fichero
correspondiente. readFile::String->O String Toma como argumento el nombre de un
fichero y devuelve el
contenido en una cadena.
A continuacin se muestran dos programas sencillos: el primer programa convierte la
entrada del usuario a maysculas.
main = interact (map toUpper)
El siguiente programa escribe en el fichero "tabla.txt" una tabla con los cuadrados de los
10 primeros nmeros naturales.
main = appendFile "tabla.txt" (show [(x,x*x) | x<-[1..10]])
Com&osici"n de O&eraciones de En!rada;Salida
Existen dos funciones de composicin de acciones de E/S. La funcin >> se utiliza
cuando el resultado de la primera accin no es interesante, normalmente, cuando es (). La
funcin (>>=) pasa el resultado de la primera accin como un argumento a la segunda
accin.
(>>=) :: O a -> (a -> O b) -> O b
(>>) :: O a -> O b -> O b
Por ejemplo
main = readFile "fentrada" >>= \cad ->
writeFile "fsalida" (map toUpper cad) >>
putStr "Conversion realizada\n"
es similar al ejemplo de la seccin anterior, salvo que se leen los contenidos del fichero
fentrada y se escriben, convirtiendo minsculas en maysculas en fsalida.

La funcin return se utiliza para definir el resultado de una operacin de Entrada/Salida.
Por ejemplo, la funcin getLine podra definirse en funcin de getChar utilizando return
para definir el resultado.
getLine :: O String
getLine = do c <- getChar
if c == '\n' then return ""
else do s <- getLine
return (c:s)
Con!rol de e+ce&ciones
El sistema de incluye un mecanismo simple de control de excepciones. Cualquier
operacin de Entrada/Salida podra lanzar una excepcin en lugar de devolver un
resultado. Las
excepciones se representan como valores de tipo OError. Mediante la funcin userError
el usuario podra lanzar tambin sus propios errores.
Las excepciones pueden ser lanzadas y capturadas mediante las funciones:
fail :: OError -> O a
catch :: O a -> (OError -> O a) -> O a
La funcin fail lanza una excepcin; la funcin catch establece un manejador que recibe
cualquier excepcin elevada en la accin protegida por l. Una excepcin es capturada
por el manejador ms reciente. Una excepcin es capturada por el manejador ms
reciente. Puesto que los manejadores capturan todas las excepciones ( no son
selectivos), el programador debe encargarse de propagar las excepciones que no desea
manejar. Si una excepcin se propaga fuera del sistema, se imprime un error de tipo
OError.
Sobrecar5a
Cuando una funcin puede utilizarse con diferentes tipos de argumentos se dice que est
sobrecargada. La funcin (+), por ejemplo, puede utilizarse para sumar enteros o para
sumar flotantes. La resolucin de la sobrecarga por parte del sistema Haskell se basa en
organizar los diferentes tipos en lo que se denominan clases de tipos.
Considrese el operador de comparacin (==). Existen muchos tipos cuyos elementos
pueden ser comparables, sin embargo, los elementos de otros tipos podran no ser
comparables. Por ejemplo, comparar la igualdad de dos funciones es una tarea
computacionalmente intratable, mientras que a menudo se desea comparar si dos listas
son iguales. De esa forma, si se toma la definicin de la funcin elem que chequea si un
elemento pertenece a una lista:
x `elem` [] = False
x `elem` (y:ys) = x == y || (x `elem` ys)
Se debera restringir la aplicacin de == a los tipos cuyos elementos son comparables.
Haskell tambin permite la inclusin de clases. Por ejemplo, podra ser interesante definir
una clase Ord que hereda todas las operaciones de Eq pero que, adems tuviese un
conjunto nuevo de operaciones:
class (Eq a) => Ord a where
(<), (<=), (>=), (>) :: a->a->Bool
max, min :: a->a->a
El contexto en la declaracin indica que Eq es una superclase de Ord (o que Ord es una
subclase de Eq), y que cualquier instancia de Ord debe ser tambin una instancia de Eq.
Las inclusiones de clase permiten reducir el tamao de los contextos: Una expresin de
tipo para una funcin que utiliza operaciones tanto de las clases Eq como Ord podra
utilizar el contexto (Ord a) en lugar de (Eq a,Ord a), puesto que Ord implica Eq. Adems,
los mtodos de las subclases pueden asumir la existencia de los mtodos de la
superclase. Por ejemplo, la declaracin de Ord en el standar prelude incluye el siguiente
mtodo por defecto:
x < y = x <=y && x/=y
Haskell tambin permite la herencia mltiple, puesto que las clases pueden tener ms de
una superclase. Los conflictos entre nombres se evitan mediante la restriccin de que una
operacin particular slo puede ser miembro de una nica clase en un mbito
determinado.
En el Standar Prelude se definen una serie de clases de tipos de propsito general.
La clase Num proporciona varias operaciones bsicas comunes a todos los tipos
numricos;
stos incluyen la suma, resta, negacin, multiplicacin, y valor absoluto:
(+),(-),(*) :: (Num a) => a -> a -> a
negate, abs :: (Num a) => a -> a
Num no proporciona un operador de divisin; se proporcionan dos clases diferentes de
operadores de divisin en dos subclases de Num que no se solapan.
La clase ntegral proporciona la operacin de divisin (div) y resto (rem), as como los
predicados even y odd (que chequean si un elemento es par o impar, respectivamente).
Las instancias estndar de ntegral son nteger (para nmeros enteros no limitados,
enteros grandes) e nt (para enteros limitados, su rango depende de la implementacin).
El resto de tipos numricos estn en la clase Fractional que proporciona el operador de
divisin (/). La subclase Floating contiene las funciones trigonomtricas, logartmicas y
exponenciales.
La subclase RealFrac de Fractional y Real proporciona una funcin properFraction que
descompone un nmero en su parte real y fraccionaria y una coleccin de funciones que
redondean a valores enteros mediante diferentes reglas:
properFraction::(Fractional a, ntegral b) => a->(b,a)
truncate, round,
floor,ceiling ::(Fractional a, ntegral b) => a -> b
La subclase RealFloat de Floating y RealFrac proporcionan algunas funciones
especializadas para acceso eficiente a los componentes de un nmero flotante, el
exponente y la mantisa. Los tipos estndar Float y Double son instancias de esta clase.
Adems de las clases numricas, en el Standar prelude se definen otras clases de tipos
como las clases Enum (cuyos elementos pueden aparecer en secuencias), x (ndices de
arrays), Show (elementos transformables en una cadena de caracteres y, por tanto,
imprimibles), Read (elementos que pueden ser ledos de una cadena de caracteres),
Monad (definicin de mnadas), etc.
M"dulos
A pesar de que en el sistema Hugs no se utiliza compilacin separada, se admite la
descomposicin en mdulos de los programas. Un mdulo define una coleccin de
valores, tipos de datos, sinnimos de tipo, clases, etc. en un entorno. Cada mdulo puede
hacer referencia a otros mdulos mediante declaraciones import, cada una de las cuales
especifica el nombre del mdulo a importar y las entidades que se importan. Los mdulos
pueden ser mutuamente recursivos.
Existe un mdulo especial, Prelude, que es importado por todos los mdulos salvo que se
indique lo contrario y un conjunto de mdulos estndar de librera que pueden ser
importados si de desea.

Вам также может понравиться