Unidad III - 2 - Haskell

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 17

¿Qué es Haskell?

Haskell es un lenguaje de programación funcional, creado a finales de 1980 por un


comité de académicos. Había una gran cantidad de lenguajes funcionales, todo el
mundo tenía su favorito, y era difícil intercambiar ideas. Así fue que un Grupo se reunió
y diseñó un nuevo lenguaje, tomando algunas de las mejores ideas de los ya existentes
(y algunas nuevas ideas). Y así nació Haskell.

Paradigmas y Lenguajes I Unidad III - 1-


Funcional

No hay, una acepción precisa sobre el término "funcional". Pero cuando decimos que
Haskell es un lenguaje funcional, solemos tener en cuenta dos cosas:
1. Las funciones tienen aquí preponderancia, y estas se pueden utilizar de la
misma forma que cualquier otro tipo de valor.
2. El significado de los programas Haskell se centra en la evaluación de
expresiones, en lugar de ejecutar instrucciones.
Esto resulta en una forma completamente diferente de pensar a la programación.

Puro

Las expresiones de Haskell son siempre referencialmente transparentes, es decir:


 ¡Sin mutaciones! Todas (las variables, las estructuras de datos...) son
inmutables. (No modificable “su significado o contenido” una vez creado)
 Las expresiones carecen de efectos secundarios o colaterales, que es una
consecuencia de la transparencia referencial.
 Llamar a la misma función con los mismos argumentos da como resultado la
misma salida en todo momento.
Esto puede parecer un poco raro en este momento. ¿Cómo es posible hacer algo sin
mutación o efectos secundarios? Bueno, desde luego requiere un cambio radical en la
forma de pensar (si se está acostumbrado a un paradigma imperativo u orientado a
objetos). Pero una vez que has hecho el cambio, hay una serie de aspectos beneficios
maravillosos:
 razonamiento ecuacional y refactorización: En Haskell siempre se puede
"reemplazar una parte por su equivalente", tal como en álgebra.
 Paralelismo: La evaluación de expresiones en paralelo es fácil, cuando se
garantiza no afectar a la otra.

Perezoso

En Haskell, las expresiones no se evalúan, hasta que sus resultados son realmente
necesarios. Esta es una característica sencilla, con consecuencias de gran alcance, lo
cual vamos a explorar. Algunas de las consecuencias son:

Paradigmas y Lenguajes I Unidad III - 2-


 Es fácil reemplazar las estructuras de control, sólo mediante la definición de una
función.
 Es posible definir y trabajar con estructuras de datos infinitas.
 Permite un estilo de programación más composicional (ver programación integral
más abajo).
Sin embargo, una desventaja importante, es que el razonamiento sobre el tiempo y el
uso del espacio se hace mucho más complicado.

Tipos
Tipado estáticamente

Cada expresión Haskell tiene asociado un tipo, dichos tipos son controlados en tiempo
de compilación. Los programas con errores de tipo no corren, ni siquiera se compilan
exitosamente.
Los sistemas de tipos estáticos pueden parecer tediosos. De hecho, pasa también en
lenguajes como C ++ y Java. Pero esto se debe a que los sistemas de tipo estático
como el C ++ y Java son insuficientemente expresivos1. Vamos a echar un vistazo de
cerca al sistema de tipos de Haskell, que:
 Ayuda a clarificar el pensamiento y a estructurar un programa expresivo.
El primer paso para escribir un programa Haskell suele ser anotar todos los
tipos. Porque el sistema de tipos de Haskell es expresivo, este es una etapa no
trivial del diseño.
 Sirve como una forma de documentación de un sistema de tipo expresivo, sólo al
mirar el tipo de una función dice mucho acerca de lo que la función puede hacer y
cómo se puede utilizar, incluso antes de haber leído una sola palabra de la
documentación escrita.
 Convierte errores en tiempo de ejecución en errores en tiempo de compilación.
 Es mucho mejor poder corregir los errores en la cabecera, que simplemente
probar sucesivamente y esperar lo mejor (aun así es muy posible tener errores en
la lógica, incluso en un programa tipado correctamente).

1
Un programa fuente es expresivo cuando no hace falta aclarar nada, sino que se entiende con solo leerlo. Un
lenguaje es expresivo si permite crear programas fuentes expresivos.

Paradigmas y Lenguajes I Unidad III - 3-


Inferencia de Tipos
Otra de las características de Haskell respecto de los tipos, es la capacidad de inferir el
tipo de una expresión, sin necesidad que el programador lo especifique. Esto lo logra
analizando las operaciones a las que una expresión es sometida. El sistema asigna el
tipo general que contenga todas las instancias posibles de la expresión.

Abstracción

 Teniendo piezas similares de código, factorizar para determinar que tienen en


común, es conocido como el proceso de abstracción. "No repitas" es un
preconcepto que se escucha a menudo en el mundo de la programación. Nada
debe estar duplicado: todas las ideas, algoritmos, y parte de los datos debe
ocurrir exactamente una vez en el código.
 Haskell es muy bueno en la abstracción: características como el polimorfismo
paramétrico, funciones de orden superior y las clases de tipo, ayudan para
evitar la repetición.

Programación integral

La programación integral implica pensar en grande: trabajar con una lista completa,
en lugar de una secuencia de elementos; desarrollar un espacio de soluciones, en
lugar de una solución individual; imaginar una trayectoria, en lugar de un tramo de
esta. El enfoque integral a menudo ofrece nuevos puntos de vista u ofrecen nuevas
perspectivas sobre un problema dado. Está muy bien complementado por la idea de la
programación proyectiva: primero resolver un problema más general, a continuación,
extraer las partes y piezas interesantes mediante la transformación del programa
general en otras más especializadas.
Por ejemplo, considere este pseudocódigo en C o Java:
lst = {2,3,5,7,11}
int total = 0;
for ( int i = 0; i < lst.length; i++) {
total = total + 3 * lst[i];
}
print total;

Este código tiene que preocuparse por los detalles de bajo nivel de iteración del arreglo
mediante el seguimiento de un índice. También se mezcla lo importante con lo
accesorio necesarios para implementar el ciclo repetitivo.

Paradigmas y Lenguajes I Unidad III - 4-


En Haskell, se crea un programa fuente con las siguientes sentencias:

lst :: [Int] -- lst es una lista de enteros


lst = [2,3,5,7,11] -- Los valores de lst son
total = sum (map (3*) lst) -- Hacer la suma de cada elemento x3
main = print total 
Se corre y la salida sera: 84

Declaraciones y Variables

Analicemos el siguiente código en Haskell:


x :: Int
x = 3
-- Observe que los comentarios se preceden con dos guiones
{- o encerrado en un par de llaves y guiones. -}
-- x = 4
main = print x 
Pruebe descomentar la 5ta. línea, y verá el error “Multiple declarations of 'x'”.
El código anterior declara una variable x con el tipo Int (:: se lee "tiene tipo") y declara
que el valor de x sea 3. Tenga en cuenta que solo este será el valor de x, mientras dure
el programa. El valor de x no cambiará más adelante. En Haskell, las variables no son
cajas mutables; no son más que nombres para los valores.

Dicho de otra manera, = no denota asignación como lo hace en muchos otros


lenguajes imperativos. Sino que denota una definición, como se lo hace en matemática.
Es decir, x = 4 no debería leerse como "4 es asignado a x" o "asignar a x un 4", sino
como "x se define como 4" o “x es 4”.

Veamos que hace el siguiente código

y :: Int
y = y + 1
main = print y 
Debido a que = denota definición en lugar de asignación, esta no se incrementa el valor
de y. En su lugar, esta afirmación se toma como una definición recursiva.

Paradigmas y Lenguajes I Unidad III - 5-


Las sucesivas producciones de y seran:.

y = y + 1
= (y + 1) + 1
= ((y + 1) + 1) + 1
=
.
.
.
En definitiva, el resultando es un loop infinito y el consiguiente error:

ERROR - C stack overflow

Paradigmas y Lenguajes I Unidad III - 6-


Tipos básicos
-- Enteros dependientes de la arquitectura
i :: Int
i = -78
29 29
Los Int del lenguaje Haskell estándar garantizan valores en rango [-2 .. 2 ] sin
embargo, el tamaño exacto depende de la arquitectura. Se puede encontrar el rango
específico de una arquitectura particular mediante la evaluación siguiente:

print (minBound :: Int, maxBound :: Int)2


La salida en una implementación sobre una cierta arquitectura será:
(-2147483648,2147483647)
Que equivale [-2 31 .. 2 31]
Sin embargo, el tipo Integer, sólo está limitado por la cantidad de memoria en la
máquina.
-- Enteros de precision Arbitraria
n :: Integer
n = 1234567890987654321987340982334987349872349874534
realmenteGrande :: Integer
realmenteGrande = 2^(2^(2^(2^2)))
numDigitos :: Int
numDigitos = length (show realmenteGrande)
-- show lo convierte en String
main = print numDigitos 
La salida es: 19729
-- Para los nros reales se utiliza el Double.
-- El Float se usa menos
d1, d2 :: Double
d1 = 4.5387
d2 = 6.2831e-4

-- Booleanos
b1, b2 :: Bool
b1 = True
b2 = False

-- caracteres Unicode
c1, c2, c3 :: Char
c1 = 'x'
c2 = 'Ø'
c3 = 'ダ'

2
Note que Haskell utiliza la convención CamelCase en la forma de darle nombre a los identificadores

Paradigmas y Lenguajes I Unidad III - 7-


-- Los Strings son listas de caracteres con una sintaxis especial
s :: String
s = "Hello, Haskell!"
Aritmetica
ex01 = 3 + 2
ex02 = 19 - 27
ex03 = 2.35 * 8.6
ex04 = 8.7 / 3.1
ex05 = mod 19 3
ex06 = 19 `mod` 3
ex07 = 7 ^ 222
ex08 = (-3) * (-7)
Note como `` acentos abiertos hacen a una función operador infijo.
i = 30 :: Int
n = 10 :: Integer
main = print (i + n)
La suma es sólo entre valores del mismo tipo numérico, y Haskell no hace la
conversión de tipos implícita. Se debe convertir explícitamente:
 fromIntegral: convierte algún tipo entero (Int o Integer) a algún otro tipo numérico.
Solución al inconveniente de inconsistencia anterior:
i = 30 :: Int
n = 10 :: Integer
main = print (i + fromIntegral n)
 round, floor, ceiling: convierte numeros punto flotante a Int.
main = print (round 1.6, floor 1.6, ceiling 1.3)
La salida es: (2, 1, 2)
round: redondea el argumento.
floor: Trunca. Devuelve el mayor entero menor al argumento.
ceiling: Devuelve el menor entero posterior al argumento.
i = 30 :: Int
main = print (i / i)
Esto devuelve un error al pretender hacer una división coma flotante entre dos enteros.
Para la división entera se debe utilizar el operador div:
i = 30 :: Int
main = print (i `div` i, 12 `div` 5)
La salida es: (1, 2)

Lógica Booleana
Los valores booleanos se pueden comparar con (&&), (| |), y not (and, or y not lógicos,
respectivamente).

Paradigmas y Lenguajes I Unidad III - 8-


ex11 = True && False
ex12 = not (False || True)
Y se puede comparar por la igualdad con (==) y (/ =), o para determinar el orden usando
(<), (>), (<=) y (> =).
ex13 = ('a' == 'a')
ex14 = (16 != 3)
ex15 = (5 > 3) && ('p' <= 'q')
ex16 = "Haskell" > "C++"
Haskell no tiene estructuras lógicas, pero si cuenta con las expresiones

if: if b then t else f

es una expresión que se evalúa como t si la expresión booleana b se evalúa como True,
y f si b se evalúa como False. Se debe tener en cuenta que las expresiones if son muy
diferentes de las sentencias if. Por ejemplo, con una sentencia if, la parte else puede ser
opcional; si el else es omitido significa que "si la expresión se evalúa como False, no se
hará nada", en cambio, en la expresión if, se requiere la parte else, de la expresión if
debe dar lugar a un cierto valor.

Definición de Funciones Básicas


Veamos el siguiente caso de función con enteros.
-- Calcule la suma para enteros >=0.
sumatorial :: Integer -> Integer
sumatorial 0 = 0
sumatorial n = n + sumatorial (n - 1)

main = print (sumatorial 10) 


Tenga en cuenta que la sintaxis para el tipo de una función sumatorial :: Integer ->
Integer dice que sumatorial es una función que toma un Integer como entrada y
produce otro Integer como salida.

Cada cláusula se evalúa en orden, de arriba hacia abajo, comprobando la primera


cláusula de equivalencia. Por ejemplo, sumatorial 0 se evalúa como 0, ya que la
primera cláusula es comprobada. En cambio, sumtorial 3 no coincide con la primera
cláusula (3 no es 0), por lo que es evaluada la segunda cláusula. Una variable como n
coincide con cualquier cosa, por lo que la segunda cláusula coincide y sumtorial 3 se
evalúa como 3 + sumtorial (3-1) (que puede ser evaluado más adelante).

Paradigmas y Lenguajes I Unidad III - 9-


Guardas

Las opciones también se pueden hacer sobre la base de expresiones booleanas


arbitrarias usando guardas, como se ve a continuación:

granizo :: Integer -> Integer


granizo n
| n `mod` 2 == 0 = n `div` 2
| otherwise = 3 * n + 1

main = print (granizo 3) 


Cualquier número de guardas pueden ser asociados a cada cláusula de una definición
de función, cada uno de los cuales es una expresión booleana. Si los patrones de la
cláusula coinciden se evalúa como True, las guardas se evalúan de arriba a abajo, y se
elige a la primera. Si ninguna de las guardas se evalúa como True, se continúa con la
siguiente cláusula.

Podríamos utilizar guardas para determinar el factorial de un número:

factorial :: Integer -> Integer


factorial n
| n < 0 = error("factorial de nro negativo")
-- argumentos negativos, llamar con el argumento entre paréntesis
| n < 2 = 1
| n > 1 = n * factorial (n-1)
main = print (factorial 5) 
Por ejemplo, supongamos que evaluamos granizo 3. Primero, 3 se compara con n, que
tiene éxito (ya que una variable coincide con cualquier cosa). A continuación, se evalúa
(n `mod` 2 == 0); es falso ya que para n = 3 al dividir por 2 no tiene resto de 0,
evaluando la segunda guarda, y el resultado de granizo 3 es, pues, 3 * 3 + 1 = 10.

Un ejemplo más complejo (pero más loco):

foo :: Integer -> Integer


foo 0 = 16
foo 1
| "Haskell" > "C++" = 3
| otherwise = 4
foo n
| n < 0 = 0
| n `mod` 17 == 2 = -43
| otherwise = n + 3
main = print [foo (-3), foo 0, foo 1, foo 36, foo 38]
el resultado será: [0, 16, 3, -43, 41]

Paradigmas y Lenguajes I Unidad III - 10-


Tuplas
Se pueden poner cosas de a duplas:
p :: (Int, Char)
p = (3, 'x')
Observe que la notación (x, y) se utiliza tanto para la dupla tipos, como para una dupla
de valores. Los elementos de una dupla pueden resultar de una coincidencia de
patrones. O podrían indicar los argumentos de una función
sumaPar :: (Int, Int) -> Int
sumaPar (x,y) = x + y

main = print (sumaPar (3,4)) 


Haskell también tiene truplas, cuádruplas y en general tuplas.
-- En linea
:set +t -- Habilitar mostrar tipos después de la evaluación
("Hola",’H’,2,3.4)
("Hola",’H’,2,3.4) :: ([Char],Char,Integer,Double)
La tupla vacía tiene el valor de () y también tiene el tipo (). Algo como
data Nada = Nada.

Uso de funciones
Para aplicar los argumentos a una función, sólo se listan los argumentos después de la
función, separados por espacios, por ejemplo:
f x y z = x + y + z
main = print (f 3 17 8) 
El ejemplo anterior se aplica la función f con los argumentos 3, 17, y 8.

Tenga en cuenta que la aplicación de funciones tiene mayor precedencia que cualquier
operador infijo. Por lo que, si tiene la intención de pasar n 1 como el segundo argumento
de f, sería incorrecto escribir f 3 n 1 7. En lugar de ello, uno debe escribir f 3 (n 1) 7.

Construcción de Listas
La lista más simple posible es la lista vacía:

emptyList = []
Otras listas se construyen a partir de la lista vacía utilizando el operador (:). Este opera
un elemento y una lista, y produce una nueva lista con el nuevo elemento al frente.
a = 1 : []
b = 3 : (1 : [])
c = [2,3,4] == 2 : 3 : 4 : []

Paradigmas y Lenguajes I Unidad III - 11-


main = print (a,b,c) 
la ejecución mostraría:
([1],[3,1],True)
Podemos ver que la notación [2,3,4] es sólo abreviatura conveniente para 2: 3: 4: [].
Tenga en cuenta también que estos son realmente listas enlazadas, y no arreglos.
Si al ejemplo del granizo le agregamos las cláusulas a partir de la 4ta linea:
granizo :: Integer -> Integer
granizo n
| n `mod` 2 == 0 = n `div` 2
| otherwise = 3 * n + 1
seqGranizo :: Integer -> [Integer]
seqGranizo 1 = [1]
seqGranizo n = n : seqGranizo (granizo n)

main = print (seqGranizo 5) 


se obtendrá la siguiente salida [5,16,8,4,2,1]
La secuencia de granizo se detendrá cuando llegue a 1. La secuencia de granizo para
un n general está compuesta por n propia, seguido de la secuencia de granizo granizo
n, es decir, el número que se obtiene mediante la aplicación de la transformación
granizo vez para n.

Funciones sobre listas


Se pueden escribir funciones sobre listas utilizando coincidencia de patrones (pattern
matching).
-- Calcula longitud sucesiva de una lista de Integers
intListLength :: [Integer] -> Integer
intListLength [] = 0
intListLength (x:xs) = 1 + intListLength xs

main = print (intListLength [1,2,3,4,5]) 


La primera clausula dice que la longitud de una lista vacía es 0.

La segunda clausula dice que si la lista de entrada se parece a (x:xs), que es, un primer
elemento x y el resto de la lista xs, entonces la longitud es 1+ la longitud de xs.

Ya que no se usa x en absoluto, podemos reemplazarlo por un guión.

intListLength (_:xs) = 1 + intListLength xs

También podemos usar patrones anidados

Paradigmas y Lenguajes I Unidad III - 12-


sumarCadaDos :: [Integer] -> [Integer]
sumarCadaDos [] = [] -- No hacer nada con una lista vacía
sumarCadaDos (x:[]) = [] -- Ni con listas con un único elem.
sumarCadaDos (x:(y:zs)) = (x + y) : sumarCadaDos zs
-- Sumar a x el primer element del resto de la lista

main = print (sumarCadaDos [1,2,3,4,5,6,7,8]) 


La salida sera:

[3,7,11,15]
Nótese cómo la última cláusula coincide con una lista que comienza con x y seguido por
... una lista a partir de y, y seguido por la lista zs. En realidad no necesitamos los
paréntesis adicionales, por lo que sumarCadaDos (x:y:zs) = ... debería ser equivalente.

Combinando Funciones
Es un buen estilo de Haskell construir funciones complejas mediante la combinación de
muchas simples.

-- El número de pasos de granizo necesario para alcanzar 1,


a partir de un número dado
granizo :: Integer -> Integer
granizo n
| n `mod` 2 == 0 = n `div` 2
| otherwise = 3 * n + 1
seqGranizo :: Integer -> [Integer]
seqGranizo 1 = [1]
seqGranizo n = n : seqGranizo (granizo n)
longGranizo :: Integer -> Integer
longGranizo n = intListLength (seqGranizo n)

main = print (longGranizo 5) 


Esto puede parecer ineficaz: generar la secuencia completa granizo primero y luego
encuentra su longitud, y que desperdicia mucha memoria ... ¿no? ¡En realidad, no es
así! Debido a la evaluación perezosa de Haskell, cada elemento de la secuencia se
genera solamente cuando se necesita, por lo que la generación de la secuencia y el
cálculo de longitud de la lista están intercalados.

Abstracciones lambda
En lugar de usar ecuaciones para definir funciones, podemos definirlas en forma
"anónima" vía una lambda abstracción. Por ejemplo, podemos escribir una función
equivalente a incremento (inc) en la forma \x -> x+1. Del mismo modo, la función add es

Paradigmas y Lenguajes I Unidad III - 13-


equivalente a \x -> \y -> x+y. Las lambda abstracciones anidadas pueden escribirse en
la siguiente otra forma equivalente \x y -> x+y. De hecho, las ecuaciones:
inc x = x+1
add x y = x+y
son formas abreviadas de:
inc = \x -> x+1
add = \x y -> x+y
Ajuste de patrones
El ajuste de patrones (“pattern matching” en inglés), consiste en una especificación
de pautas que deben ser seguidas por los datos, los cuales pueden ser descompuestos
permitiéndonos acceder a sus componentes.
Podemos separar el cuerpo que define el comportamiento de una función en varias
partes, de forma que el código quede mucho más elegante, limpio y fácil de leer.
Podemos usar el ajuste de patrones con cualquier tipo de dato: números, caracteres,
listas, tuplas, etc. Vamos a crear una función muy trivial que compruebe si el número
que le pasamos es un siete o no.
lucky :: (Integral a) => a -> String
lucky 7 = "¡El siete de la suerte!"
lucky x = "Lo siento, ¡no es tu día de suerte!"
Cuando llamamos a lucky, los patrones son verificados de arriba a abajo y cuando un
patrón concuerda con el valor asociado, se utiliza el cuerpo de la función asociado. En
este caso, la única forma de que un número concuerde con el primer patrón es que
dicho número sea 7. Si no lo es, se evaluará el siguiente patrón, el cual coincide con
cualquier valor y lo liga a x. Sin embargo con él:
sayMe :: (Integral a) => a -> String
sayMe 1 = "¡Uno!"
sayMe 2 = "¡Dos!"
sayMe 3 = "¡Tres!"
sayMe 4 = "¡Cuatro!"
sayMe 5 = "¡Cinco!"
sayMe x = "No entre uno 1 y 5"
Ten en cuenta que si movemos el último patrón (el más general) al inicio, siempre
obtendríamos "No entre uno 1 y 5"como respuesta, ya que el primer patrón encajaría
con cualquier número y no habría posibilidad de que se comprobarán los demás
patrones.

Paradigmas y Lenguajes I Unidad III - 14-


La función factorial podemos implementarla en forma recursiva. Empezamos diciendo
que el factorial de 0 es 1. Luego decimos que el factorial de cualquier otro número
entero positivo es ese entero multiplicado por el factorial de su predecesor.
factorial :: (Integral a) => a -> a
factorial 0 = 1
factorial n = n * factorial (n - 1)
Se ha definido el factorial de 0 para que sea 1, y como se encuentra con ese patrón
antes que el otro más general, se obtendrá 1. Así que el resultado equivale a 3 * (2 * (1
* 1)). En cambio, sí se hubiese escrito el segundo patrón al inicio, hubiese aceptado
todos los números incluyendo el 0 y el cálculo nunca terminaría. Por este motivo el
orden es importante a la hora de definir los patrones y siempre es mejor definir los
patrones más específicos al principio dejando los más generales al final.

Los patrones también pueden fallar. Si se define una función:


charName :: Char -> String
charName 'a' = "Alberto"
charName 'b' = "Benjamín"
charName 'c' = "Cecilia"
y se la llama con un valor no considerados, esto es lo que pasa:
> charName 'a'
"Alberto"
> charName 'b'
"José"
> charName 'h'
"*** Exception: tut.hs:(53,0)-(55,21): Non-exhaustive patterns in
function charName
Esto pasa por tener un ajuste de patrones no exhaustivo. Cuando se utilizan patrones
siempre se debe incluir uno general para asegurar que el programa no falle.

Paradigmas y Lenguajes I Unidad III - 15-


No hay que tener miedo a los mensajes de error
Los mensajes de error pueden ser bastante largo e intimidantes. Sin embargo, son
largos, no porque sean oscuros, sino debido a que contienen una gran cantidad de
información. Analice los siguientes ejemplos:
Main> 'x' ++ "foo"
ERROR - Type error in application
*** Expression : 'x' ++ "foo"
*** Term : 'x'
*** Type : Char
*** Does not match : [Char]
En primer lugar:

*** Expression : 'x' ++ "foo"


Nos indica un error en esta expresión en el término
*** Term : 'x'
Que es de tipo
*** Type : Char
Y debería ser de tipo
*** Does not match : [Char]

Es decir el operador (++) concatena cadenas e intentamos hacerlo con tipos


incompatibles.
Algunas recomendaciones
:names Lista de nombres que coincidan con cualquiera de los patrones de la lista y se
definen en cualquiera de los módulos cargados actualmente. Los patrones se asemejan
a los patrones de nombre de archivo:

 * cualquier subcadena

 ? coincide con cualquier carácter,

 [caracteres] coincide con cualquier de los caracteres

 char-char un rango de caracteres

 \ char char indicados.

:browse all Muestra todos los nombres en su ámbito dentro de los módulos. Si no se
ven los módulos, solo describe el módulo actual. También indica tipos, parámetros y
salidas de las funciones dentro del ámbito.

:load [archivos o módulos]

Paradigmas y Lenguajes I Unidad III - 16-


Limpia todos los archivos excepto el módulo vacío Hugs, el Prelude de Haskell 98 y los
módulos que este usa, luego invoca los archivos o módulos especificados (si los
hubieran). El último módulo invocado será el módulo actual.

:reload

Borrar todos los archivos excepto los módulos vacíos Prelude de Haskell 98 y los
módulos que utiliza, y vuelve a cargar todos los módulos cargados previamente.

: edit [archivo]

El comando edit inicia el programa editor para modificar o visualizar un módulo de


Haskell. Hugs suspende hasta que el editor termina, y luego vuelve a cargar los módulos
cargados actualmente. Si no se especifica ningún nombre de archivo, Hugs edita el
módulo actual. La opción -E se utilizar para configurar en Hugs un editor de elección.

: find [archivo]

Edita el módulo del archivo encontrado.

: main [argumentos ...]

Ejecuta la función principal, con la función getArgs devolución de los valores


especificados para los argumentos. Esto es útil para los programas de pruebas de la
intérprete.

Si su importación módulo principal System.Environment, usted puede conseguir el


mismo efecto usando withArgs, por ejemplo,

withArgs ["Hola", "mundo"] main

:! [command]

Invoca el Shell con el comando indica. Si el comando es omitido abre una sesión del
shell.

:set -t

Deshabilita los tipos en la salida.

:set +t

Imprimir el tipo de cada expresión evaluada (por defecto: desactivado). Normalmente


Hugs simplemente imprime el valor de cada expresión evaluada.

Fuentes:

https://www.fpcomplete.com/school/starting-with-haskell/introduction-to-haskell/1-
haskell-basics

https://www.haskell.org/hugs/pages/users_guide/info-commands.html

http://aprendehaskell.es/

Paradigmas y Lenguajes I Unidad III - 17-

También podría gustarte