Difference between revisions of "Aprende Haskell en 10 minutos"
(Start Spanish translation) |
(Add first Spanish translation) |
||
Line 1: | Line 1: | ||
== Introducción == |
== Introducción == |
||
+ | |||
+ | Haskell es un lenguaje funcional (donde todo se hace con llamadas a funciones), |
||
+ | estático, implícitamente tipado (los [[type|tipo]]s 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]], [[BSD | FreeBSD]], [[Mac OS x|MacOS]], [[Windows]], y |
||
+ | [[Solaris]]. Una vez instalado [[GHC]], se tienen dos programas que son de |
||
+ | interés ahora: <tt>ghc</tt>, y <tt>[[GHC/GHCi | ghci]]</tt>. 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. |
||
+ | |||
+ | |||
+ | == Expresiones simples == |
||
+ | |||
+ | Es posible escribir varias expresiones matemáticas directamente en <tt>ghci</tt> |
||
+ | y obtener un resultado. <tt>Prelude</tt> 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 |
||
+ | <hask>++</hask>. |
||
+ | |||
+ | Prelude> "Hello" |
||
+ | "Hello" |
||
+ | Prelude> "Hello" ++ ", Haskell" |
||
+ | "Hello, Haskell" |
||
+ | |||
+ | Las [[function|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 |
||
+ | |||
+ | == La consola == |
||
+ | |||
+ | Las [[Introduction to IO|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 <hask>putStr</hask> y <hask>putStrLn</hask> imprimen cadenas en la |
||
+ | terminal. La función <hask>print</hask> imprime cualquier tipo de valor. (Si |
||
+ | se hace <hask>print</hask> de una cadena, tendrá comillas). |
||
+ | |||
+ | Si necesita múltiples acciones I/O en una expresión, puede usar un bloque |
||
+ | <hask>do</hask>. 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 |
||
+ | <hask>String</hask>) o <hask>readLn</hask> (que retorna un valor de cualquier |
||
+ | tipo que se desee). El símbolo <hask> <- </hask> 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 <hask>do</hask>. Si se suprimen las |
||
+ | llaves y los punto y coma, la indentación se vuelve importante. Esto no |
||
+ | funciona muy bien en <tt>ghci</tt>, pero intente poniéndolo en un fichero (por |
||
+ | ejemplo, <tt>Test.hs</tt>) y compilelo. |
||
+ | |||
+ | <haskell> |
||
+ | main = do putStrLn "What is 2 + 2?" |
||
+ | x <- readLn |
||
+ | if x == 4 |
||
+ | then putStrLn "You're right!" |
||
+ | else putStrLn "You're wrong!" |
||
+ | </haskell> |
||
+ | |||
+ | Puede compilar con <tt>ghc --make Test.hs</tt>, y el resultado se llamará |
||
+ | <tt>Test</tt>. (en [[Windows]], <tt>Test.exe</tt>) Se tiene una expresión |
||
+ | <hask>if</hask> como un extra. |
||
+ | |||
+ | El primer carácter no blanco luego de <hask>do</hask> es especial. En este |
||
+ | caso, es la <tt>p</tt> en <hask>putStrLn</hask>. Cada linea que empieza en la |
||
+ | misma columna que la <hask>p</hask> es otra instrucción en el bloque |
||
+ | <hask>do</hask>. Si se indenta más, se vuelve parte de la instrucción anterior. |
||
+ | Si se indenta menos, se termina el bloque <hask>do</hask>. Esto se llama |
||
+ | "layout", y Haskell lo usa para evitar el uso de puntos y coma y llaves todo el |
||
+ | tiempo. (Las frases <hask>then</hask> y <hask>else</hask> 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!). |
||
+ | |||
+ | |||
+ | == Tipos Simples == |
||
+ | |||
+ | Hasta ahora no se a mencionado ni un solo [[type|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 |
||
+ | <hask>::</hask> para hacerlo. |
||
+ | |||
+ | Prelude> 5 :: Int |
||
+ | 5 |
||
+ | Prelude> 5 :: Double |
||
+ | 5.0 |
||
+ | |||
+ | [[type|Tipos]] (y las [[class|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 [[Studly capitals||convención]]. |
||
+ | |||
+ | Se puede preguntar a <tt>ghci</tt> 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, <hask>[Char]</hask> es otra forma de decir |
||
+ | <hask>String</hask>. Vea la [[#Structured data|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: |
||
+ | |||
+ | * <hask>42</hask> se puede usar como cualquier tipo numérico. (Esta es la razón por la que podemos declarar <hask>5</hask> como <hask>Int</hask> o como <hask>Double</hask>). |
||
+ | * <hask>42.0</hask> puede ser cualquier tipo fraccionario, pero no un tipo integral. |
||
+ | * <hask>gcd 15 20</hask> (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): |
||
+ | |||
+ | * <hask>Int</hask> es un entero con al menos 30 bits de precisión. |
||
+ | * <hask>Integer</hask> es un entero con precisión ilimitada. |
||
+ | * <hask>Float</hask> es un numero de punto flotante con precisión simple. |
||
+ | * <hask>Double</hask> es un numero de punto flotante con precisión doble. |
||
+ | * <hask>Rational</hask> es un tipo fraccionario, sin error de redondeo. |
||
+ | |||
+ | Los cinco son '''instancias''' de la clase de tipo <hask>Num</hask>. Los |
||
+ | primeros dos son '''instancias''' de <hask>Integral</hask>, y los últimos tres |
||
+ | son '''instancias''' de <hask>Fractional</hask>. |
||
+ | |||
+ | 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 <hask>()</hask>, que se pronuncia "unidad". Solo tiene un |
||
+ | valor, que se escribe también como <hask>()</hask> y se pronuncia "unidad" de |
||
+ | igual manera. |
||
+ | |||
+ | Prelude> () |
||
+ | () |
||
+ | Prelude> :t () |
||
+ | () :: () |
||
+ | |||
+ | Se puede pensar en este tipo como en <tt>void</tt> de la familia de lenguajes de |
||
+ | C. Se puede retornar <hask>()</hask> desde una acción de I/O cuando no se |
||
+ | quiere retornar nada en particular. |
||
+ | |||
+ | |||
+ | == 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 <hask>:</hask> agrega un elemento al principio de la lista. (Es la |
||
+ | versión de Haskell de <tt>cons</tt> 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 <hask>zip</hask>, 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 [[how to work on lists|como trabajar con listas]]. |
||
+ | |||
+ | |||
+ | == Definición de [[Function|funciones]] == |
||
+ | |||
+ | Habíamos escrito la definición de [[Introduction to Haskell IO/Actions|acciones |
||
+ | IO]], llamada <hask>main</hask>: |
||
+ | |||
+ | <haskell> |
||
+ | main = do putStrLn "What is 2 + 2?" |
||
+ | x <- readLn |
||
+ | if x == 4 |
||
+ | then putStrLn "You're right!" |
||
+ | else putStrLn "You're wrong!" |
||
+ | </haskell> |
||
+ | |||
+ | Ahora podemos suplementar escribiendo la definición de una |
||
+ | ''[[function|función]]'' y llamándola <hask>factorial</hask>. También agregaré |
||
+ | un modulo de cabecera. |
||
+ | |||
+ | <haskell> |
||
+ | 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!" |
||
+ | </haskell> |
||
+ | |||
+ | Construyalo otra vez usando <tt>ghc --make Test.hs</tt>. 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 <hask>factorial 5</hask> sin paréntesis. |
||
+ | |||
+ | Podemos preguntar el [[type|tipo]] a <tt>ghci</tt>. |
||
+ | |||
+ | $ 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 <hask> -> </hask>, y luego el tipo del resultado. (Este ejemplo también |
||
+ | tiene la clase de tipo <hask>Num</hask>). |
||
+ | |||
+ | El factorial se puede simplificar escribiendo el análisis de casos. |
||
+ | |||
+ | <haskell> |
||
+ | factorial 0 = 1 |
||
+ | factorial n = n * factorial (n - 1) |
||
+ | </haskell> |
||
+ | |||
+ | |||
+ | == Sintaxis conveniente == |
||
+ | |||
+ | Algo más de [[:Category:Syntax|sintaxis]] es útil. |
||
+ | |||
+ | <haskell> |
||
+ | secsToWeeks secs = let perMinute = 60 |
||
+ | perHour = 60 * perMinute |
||
+ | perDay = 24 * perHour |
||
+ | perWeek = 7 * perDay |
||
+ | in secs / perWeek |
||
+ | </haskell> |
||
+ | |||
+ | La expresión <hask>let</hask> define nombres temporales. (Esto es usando layout |
||
+ | otra vez. Se puede usar {llaves}, y separar los nombres con punto y comas, si se |
||
+ | prefiere). |
||
+ | |||
+ | <haskell> |
||
+ | classify age = case age of 0 -> "newborn" |
||
+ | 1 -> "infant" |
||
+ | 2 -> "toddler" |
||
+ | _ -> "senior citizen" |
||
+ | </haskell> |
||
+ | |||
+ | La expresión <hask>case</hask> produce ramas de decisión. La etiqueta especial |
||
+ | <hask>_</hask> significa "cualquier otra cosa". |
||
+ | |||
+ | == Usando librerías == |
||
+ | |||
+ | Todo lo que se ha usado hasta ahora es parte del [[Prelude|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: |
||
+ | |||
+ | * [[Applications and libraries/Data structures |Useful data structures]] |
||
+ | * [[Applications and libraries/Concurrency and parallelism |Concurrent and parallel programming]] |
||
+ | * [[Applications and libraries/GUI libraries | Graphics and GUI libraries]] |
||
+ | * [[Applications and libraries/Network | Networking, POSIX, and other system-level stuff]] |
||
+ | * Two test frameworks, QuickCheck and HUnit |
||
+ | * Regular expressions and predictive parsers |
||
+ | * More... |
||
+ | |||
+ | * [[Applications and libraries/Data structures |Estructuras de datos útiles]] |
||
+ | * [[Applications and libraries/Concurrency and parallelism |Concurrencia y programación paralela]] |
||
+ | * [[Applications and libraries/GUI libraries | Librerías de GUI y gráficos]] |
||
+ | * [[Applications and libraries/Network | Redes, POSIX, y otras cosas al nivel del sistema]] |
||
+ | * Dos frameworks de tests, QuickCheck y HUnit |
||
+ | * Expresiones regulares y parsers |
||
+ | * Más... |
||
+ | |||
+ | <haskell> |
||
+ | 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 |
||
+ | </haskell> |
||
+ | |||
+ | La palabra reservada <hask>import</hask> dice que se usará el código de |
||
+ | <hask>Data.Map</hask> y que tendrá el prefijo de <hask>M</hask>. (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 <hask>as</hask>). |
||
+ | |||
+ | 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 |
||
+ | [[applications and libraries|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. |
||
+ | |||
+ | == Temas que no encajan en 10 minutos == |
||
+ | |||
+ | * [[:Category:Language | Tipos de datos avanzados]] |
||
+ | ** Aritmética de listas |
||
+ | ** [[List comprehension|Comprensión de listas]] |
||
+ | ** [[Type#Type and newtype | Sinónimos de tipos]] |
||
+ | ** [[Type|data vs newtype]] (y [[Newtype|aquí]]) |
||
+ | ** [[Class | Clases de tipos e instancias]] |
||
+ | * [[:Category:Syntax |Sintaxis avanzada]] |
||
+ | ** [[Operator|Operadores]] |
||
+ | ** [[Infix operator |(+) y `foo`]] |
||
+ | ** [[Fixity declaration|Declaraciones de fijación]] |
||
+ | * Funciones avanzadas |
||
+ | ** [[Currying]] |
||
+ | ** [[Lambda abstraction|Abstracciones lambda]] |
||
+ | ** [[Section of an infix operator |Secciones]] |
||
+ | * [[:Category:Monad |Monads]] |
||
+ | * [[Tutorials/Programming Haskell/String IO |I/O]] |
||
+ | ** Leer ficheros |
||
+ | ** Escribir ficheros |
Latest revision as of 16:35, 9 October 2016
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.
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
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!).
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 declarar5
comoInt
o comoDouble
).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.
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.
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)
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".
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:
- Useful data structures
- Concurrent and parallel programming
- Graphics and GUI libraries
- Networking, POSIX, and other system-level stuff
- Two test frameworks, QuickCheck and HUnit
- Regular expressions and predictive parsers
- More...
- Estructuras de datos útiles
- Concurrencia y programación paralela
- Librerías de GUI y gráficos
- Redes, POSIX, y otras cosas al nivel del sistema
- Dos frameworks de tests, QuickCheck y HUnit
- Expresiones regulares y parsers
- Más...
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.
Temas que no encajan en 10 minutos
- Tipos de datos avanzados
- Aritmética de listas
- Comprensión de listas
- Sinónimos de tipos
- data vs newtype (y aquí)
- Clases de tipos e instancias
- Sintaxis avanzada
- Funciones avanzadas
- Monads
- I/O
- Leer ficheros
- Escribir ficheros