Personal tools

Aprende Haskell en 10 minutos

From HaskellWiki

Jump to: navigation, search

Contents

1 Introducción

Haskell es un lenguaje funcional (donde todo se hace con llamadas a funciones), estático, implícitamente tipado (los tipos los revisa el compilador, pero no hace falta declararlos), perezoso (nada se hace hasta que es completamente necesario). Los lenguajes populares más parecidos son la familia ML (que no son, sin embargo, lenguajes perezosos).

El compilador de Haskell más común es GHC. Puede descargarlo de http://www.haskell.org/ghc/download . Los ejecutables de GHC están disponibles para GNU/Linux, FreeBSD, MacOS, Windows, y Solaris. Una vez instalado GHC, se tienen dos programas que son de interés ahora: ghc, y ghci. El primero compila librerías y aplicaciones escritas en Haskell a código binario. El segundo es un interprete que permite escribir código Haskell y obtener un resultado inmediato.


2 Expresiones simples

Es posible escribir varias expresiones matemáticas directamente en ghci y obtener un resultado. Prelude es el prompt por defecto de GHCi.

 Prelude> 3 * 5
 15
 Prelude> 4 ^ 2 - 1
 15
 Prelude> (1 - 5)^(3 * 2 - 4)
 16

Las cadenas van entre "comillas dobles." Puede concatenarlas con

++
.
 Prelude> "Hello"
 "Hello"
 Prelude> "Hello" ++ ", Haskell"
 "Hello, Haskell"

Las funciones se llaman colocando los argumentos directamente después del nombre de la función. No hay paréntesis en la llamada a la función:

 Prelude> succ 5
 6
 Prelude> truncate 6.59
 6
 Prelude> round 6.59
 7
 Prelude> sqrt 2
 1.4142135623730951
 Prelude> not (5 < 3)
 True
 Prelude> gcd 21 14
 7

3 La consola

Las Acciones I/O se pueden usar para leer y escribir en la consola. Algunas son:

 Prelude> putStrLn "Hello, Haskell"
 Hello, Haskell
 Prelude> putStr "No newline"
 No newline
 Prelude> print (5 + 4)
 9
 Prelude> print (1 < 2)
 True
Las funciones
putStr
y
putStrLn
imprimen cadenas en la terminal. La función
print
imprime cualquier tipo de valor. (Si se hace
print
de una cadena, tendrá comillas).

Si necesita múltiples acciones I/O en una expresión, puede usar un bloque

do
. Las acciones se separan con punto y coma.
 Prelude> do { putStr "2 + 2 = " ; print (2 + 2) }
 2 + 2 = 4
 Prelude> do { putStrLn "ABCDE" ; putStrLn "12345" }
 ABCDE
 12345

La lectura se puede hacer con <haske>getLine</hask> (que retorna una cadena

String
) o
readLn
(que retorna un valor de cualquier tipo que se desee). El símbolo
 <-
se usa para asignar un nombre

al resultado de la acción I/O.

 Prelude> do { n <- readLn ; print (n^2) }
 4
 16

(El 4 fue la etrada. El 16 la salida)

Existe otra forma de escribir los bloques
do
. Si se suprimen las

llaves y los punto y coma, la indentación se vuelve importante. Esto no funciona muy bien en ghci, pero intente poniéndolo en un fichero (por ejemplo, Test.hs) y compilelo.

main = do putStrLn "What is 2 + 2?"
          x <- readLn
          if x == 4
              then putStrLn "You're right!"
              else putStrLn "You're wrong!"

Puede compilar con ghc --make Test.hs, y el resultado se llamará Test. (en Windows, Test.exe) Se tiene una expresión

if
como un extra. El primer carácter no blanco luego de
do
es especial. En este caso, es la p en
putStrLn
. Cada linea que empieza en la misma columna que la
p
es otra instrucción en el bloque
do
. Si se indenta más, se vuelve parte de la instrucción anterior. Si se indenta menos, se termina el bloque
do
. Esto se llama

"layout", y Haskell lo usa para evitar el uso de puntos y coma y llaves todo el

tiempo. (Las frases
then
y
else
deben estar indentadas

por esta razón. Si empiezan en la misma columna, serán instrucciones separadas, lo que sería incorrecto).

(Nota: No indente con tabulaciones si está usando "layout". Técnicamente funcionará si usa tabulaciones de 8 espacios, pero no es una buena idea. Tampoco use fuentes proporcionales que, aparentemente algunas personas usan, incluso para programar!).


4 Tipos Simples

Hasta ahora no se a mencionado ni un solo tipo. Esto es por que Haskell hace inferencia de tipos. Generalmente no es necesario declararlos a menos que se así se quiera. Si desea declarar tipos, se puede usar

::
para hacerlo.
 Prelude> 5 :: Int
 5
 Prelude> 5 :: Double
 5.0

Tipos (y las clases de tipos, de las cuales se hablará luego) siempre empiezan con una letra mayúscula. Las variables siempre empiezan con una letra minúscula. Esta es una regla del lenguaje, no una |convención.

Se puede preguntar a ghci que tipo se ha elegido para algo. Esto es útil por que generalmente no hay necesidad de declarar los tipos.

 Prelude> :t True
 True :: Bool
 Prelude> :t 'X'
 'X' :: Char
 Prelude> :t "Hello, Haskell"
 "Hello, Haskell" :: [Char]
(En caso de que lo haya notado,
[Char]
es otra forma de decir
String
. Vea la sección sobre listas luego.)

Algunas cosas se tornan más interesantes con los números.

 Prelude> :t 42
 42 :: (Num t) => t
 Prelude> :t 42.0
 42.0 :: (Fractional t) => t
 Prelude> :t gcd 15 20
 gcd 15 20 :: (Integral t) => t

Estos tipos usan "Clases de tipo". Significan:

  • 42
    se puede usar como cualquier tipo numérico. (Esta es la razón por la que podemos declarar
    5
    como
    Int
    o como
    Double
    ).
  • 42.0
    puede ser cualquier tipo fraccionario, pero no un tipo integral.
  • gcd 15 20
    (que es una llamada de función) puede ser cualquier tipo integral, pero no un tipo fraccionario.

Existen 5 tipos numéricos en el "preludio" de Haskell (la parte de la librería que se obtiene sin tener que importar nada):

  • Int
    es un entero con al menos 30 bits de precisión.
  • Integer
    es un entero con precisión ilimitada.
  • Float
    es un numero de punto flotante con precisión simple.
  • Double
    es un numero de punto flotante con precisión doble.
  • Rational
    es un tipo fraccionario, sin error de redondeo.
Los cinco son instancias de la clase de tipo
Num
. Los primeros dos son instancias de
Integral
, y los últimos tres son instancias de
Fractional
.

Poniéndolo todo junto:

 Prelude> gcd 42 35 :: Int
 7
 Prelude> gcd 42 35 :: Double
 <interactive>:1:0:
     No instance for (Integral Double)
El tipo final es un
()
, que se pronuncia "unidad". Solo tiene un valor, que se escribe también como
()
y se pronuncia "unidad" de

igual manera.

 Prelude> ()
 ()
 Prelude> :t ()
 () :: ()

Se puede pensar en este tipo como en void de la familia de lenguajes de

C. Se puede retornar
()
desde una acción de I/O cuando no se

quiere retornar nada en particular.


5 Datos estructurados

Los tipos de datos básicos se pueden combinar fácilmente de dos maneras: listas, que van entre [corchetes], y tuplas, que van entre (paréntesis).

Las listas se usan para mantener múltiples valores del mismo tipo.

 Prelude> [1, 2, 3]
 [1,2,3]
 Prelude> [1 .. 5]
 [1,2,3,4,5]
 Prelude> [1, 3 .. 10]
 [1,3,5,7,9]
 Prelude> [True, False, True]
 [True,False,True]

Las cadenas solamente son listas de caracteres.

 Prelude> ['H', 'e', 'l', 'l', 'o']
 "Hello"
El operador
:
agrega un elemento al principio de la lista. (Es la

versión de Haskell de cons de la familia de lenguajes de Lisp).

 Prelude> 'C' : ['H', 'e', 'l', 'l', 'o']
 "CHello"

Las tuplas mantienen un numero fijo de valores, que pueden tener tipos distintos.

 Prelude> (1, True)
 (1,True)
 Prelude> zip [1 .. 5] ['a' .. 'e']
 [(1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e')]
El ultimo ejemplo usa
zip
, una función que convierte dos listas en

una lista de tuplas.

Los tipos son probablemente lo que se esperaría.

 Prelude> :t ['a' .. 'c']
 ['a' .. 'c'] :: [Char]
 Prelude> :t [('x', True), ('y', False)]
 [('x', True), ('y', False)] :: [(Char, Bool)]

Las listas se usan mucho en Haskell. Has varias funcionas que haces cosas útiles con ellas.

 Prelude> [1 .. 5]
 [1,2,3,4,5]
 Prelude> map (+ 2) [1 .. 5]
 [3,4,5,6,7]
 Prelude> filter (> 2) [1 .. 5]
 [3,4,5]

Hay dos funciones para pares ordenados (tuplas de dos elementos):

 Prelude> fst (1, 2)
 1
 Prelude> snd (1, 2)
 2
 Prelude> map fst [(1, 2), (3, 4), (5, 6)]
 [1,3,5]

También puede revisar como trabajar con listas.


6 Definición de funciones

Habíamos escrito la definición de acciones IO, llamada
main
:
main = do putStrLn "What is 2 + 2?"
          x <- readLn
          if x == 4
              then putStrLn "You're right!"
              else putStrLn "You're wrong!"

Ahora podemos suplementar escribiendo la definición de una

función y llamándola
factorial
. También agregaré

un modulo de cabecera.

module Main where
 
factorial n = if n == 0 then 1 else n * factorial (n - 1)
 
main = do putStrLn "What is 5! ?"
          x <- readLn
          if x == factorial 5
              then putStrLn "You're right!"
              else putStrLn "You're wrong!"

Construyalo otra vez usando ghc --make Test.hs. y,

 $ ./Test
 What is 5! ?
 120
 You're right!

Ahí tenemos una función. Tal como las que vienen por defecto, se puede llamar

como
factorial 5
sin paréntesis.

Podemos preguntar el tipo a ghci.

 $ ghci Test.hs
 << GHCi banner >>
 Ok, modules loaded: Main.
 Prelude Main> :t factorial
 factorial :: (Num a) => a -> a

Los tipos de las funciones se escriben como el tipo del los argumentos, seguidos

de
 ->
, y luego el tipo del resultado. (Este ejemplo también tiene la clase de tipo
Num
).

El factorial se puede simplificar escribiendo el análisis de casos.

factorial 0 = 1
factorial n = n * factorial (n - 1)


7 Sintaxis conveniente

Algo más de sintaxis es útil.

secsToWeeks secs = let perMinute = 60
                       perHour   = 60 * perMinute
                       perDay    = 24 * perHour
                       perWeek   =  7 * perDay
                   in  secs / perWeek
La expresión
let
define nombres temporales. (Esto es usando layout

otra vez. Se puede usar {llaves}, y separar los nombres con punto y comas, si se prefiere).

classify age = case age of 0 -> "newborn"
                           1 -> "infant"
                           2 -> "toddler"
                           _ -> "senior citizen"
La expresión
case
produce ramas de decisión. La etiqueta especial
_
significa "cualquier otra cosa".

8 Usando librerías

Todo lo que se ha usado hasta ahora es parte del Preludio, que es un conjunto de funciones Haskell que siempre están disponibles sin solicitarlas.

La mejor forma de volverse productivo en Haskell (aparte de la práctica!) es familiarizarse con otras [[Applications and libraries|librerías] que hacen lo que necesite. La documentación sobre las librerías estándar se encuentra en [http://haskell.org/ghc/docs/latest/html/libraries/ http://haskell.org/ghc/docs/latest/html/libraries/]. Hay varios módulos con:

module Main where
 
import qualified Data.Map as M
 
errorsPerLine = M.fromList
    [ ("Chris", 472), ("Don", 100), ("Simon", -5) ]
 
main = do putStrLn "Who are you?"
          name <- getLine
          case M.lookup name errorsPerLine of
              Nothing -> putStrLn "I don't know you"
              Just n  -> do putStr "Errors per line: "
                            print n
La palabra reservada
import
dice que se usará el código de
Data.Map
y que tendrá el prefijo de
M
. (Eso es

necesario por que algunas funciones tienen el mismo nombre que las del preludio.

La mayoría de librerías no necesitan la parte
as
).

Si se quiere algo que no está en la librería estándar, intente buscar en http://hackage.haskell.org/packages/hackage.html o en la pagina de esta wiki aplicaciones y librerías. Esta es una colección de varias librerías distintas escritas por muchas personas para Haskell. Una vez que se obtienen una librería, se puede extraerla y en el directorio obtenido hacer:

 runhaskell Setup configure
 runhaskell Setup build
 runhaskell Setup install

En un sistema UNIX, se puede necesitar ser root para esta ultima parte.

9 Temas que no encajan en 10 minutos