Guia Rc3a1pida Haskell PDF
Guia Rc3a1pida Haskell PDF
Guia Rc3a1pida Haskell PDF
Cadenas
Esta gua rpida abarca los elementos fundamentales del lenguaje Haskell: sintaxis, palabras clave
y otros elementos. Se presenta como un archivo
ejecutable de Haskell y tambin como un documento para impresin. Cargue la fuente en su intrprete favorito para jugar con los ejemplos de
cdigo mostrados.
Sintaxis Bsica
Un comentario de una sola lnea comienza con --
y se extiende hasta el final de la lnea. Los comentarios de varias lneas comienzan con {- y se
extienden hasta -}. Los comentarios pueden ser
anidados.
Los comentarios antes de las definiciones de
funcin deben comenzar con {- | y los que estn junto a los tipos de parmetros con -- ^ para
que sean compatibles con Haddock, un sistema
para documentar cdigo en Haskell.
Palabras Reservadas
Las siguientes palabras estn reservadas para
Haskell. Es un error de sintaxis darle a una variable o a una funcin uno de estos nombres.
case
class
data
deriving
do
else
if
Comentarios
import
in
infix
infixl
infixr
instance
let
of
module
newtype
then
type
where
Cdigos de escape
Los siguientes cdigos de
escape pueden ser utilizados en caracteres o cadenas:
\n, \r, \f, etc. Los cdigos estndar para
fin de lnea, retorno de carro, avance de
lnea, etc.
\72, \x48,
texto110 Un caracter con el valor 72 en decimal, hexadecimal y octal, respectivamente.
\& El caracter de escape null, es utilizado
para que los cdigos de escape numricos puedan aparecer junto de las literales
numricas. Es equivalente a y por lo tanto
no puede ser utilizado en literales de caracter.
Nmeros
string1 = "My long \
\string."
El rea entre las barras diagonales inversas es ignorada. Los fines de lnea en la cadena deben ser
representados explcitamente:
My long string.
Mientras string2 evala como:
My long
string.
1
Enumeraciones
[1..10] Lista de nmeros 1, 2, . . ., 10.
[100..] Lista infinita de nmeros 100,
101, 102, . . . .
[110..100] Lista vaca; los rangos solamente avanzan hacia adelante.
[0, -1 ..] Enteros negativos.
[-110..-100] Error de sintaxis; necesita
[-110.. -100] por los negativos.
[1,3..99], [-1,3..99] Lista de 1 a 99, de
2 en 2; y de -1 a 99, de 4 en 4.
jgbailey@codeslower.com
square2 x = { x * x; }
Sin embargo, esto funciona bien:
square2 x = result
where { result = x * x; }
Definicin de funciones
Aplique una sangra
de al menos un espacio a partir del nombre de la
funcin:
Declaraciones, Etc.
La siguiente seccin describe las reglas para la
declaracin de funciones, las listas por comprensin, y otras reas del lenguaje.
Definicin de Funciones
square x =
x * x
square x =
x2
where x2 =
x * x
Let
Aplique sangra sobre el cuerpo del let al
menos un espacio a partir de la primera definicin
en el let. Si el let aparece por s solo en una lnea,
el cuerpo de cualquier definicin debe aparecer en
la columna despus del let:
square x =
let x2 =
x * x
in x2
2
square x = x * x
Todos los nombres de funcin deben comenzar con
letra minscula o _. Es un error de sintaxis de
cualquier otra forma.
Comparacin de patrones
Se pueden definir
varias clusulas de una funcin haciendo comparacin de patrones en los valores de los argumentos. Aqu, la funcin agree tiene cuatro casos
separados:
toLowerStr [] = []
toLowerStr str = map toLower str
Ntese que aqu str es similar a _ en que va a coincidir con lo que sea; la nica diferencia es que al
valor que coincide tambin se le da un nombre.
n + k Patterns
Esta (a veces controversial) comparacin de patrones hace fcil coincidir con ciertos tipos de expresiones numricas. La idea es
definir un caso base (la porcin n) con un
nmero constante para que coincida, y despus
definir las coincidencias (la porcin k) como
sumas sobre el caso base. Por ejemplo, esta es una
forma ineficiente de determinar si un nmero es o
no par:
c 2009 Justin Bailey.
isEven 0 = True
isEven 1 = False
isEven (n + 2) = isEven n
Captura de Argumentos
La captura de argumentos es til para comparar un patrn y utilizarlo, sin declarar una variable extra. Utilice un
smbolo @ entre el patrn a coincidir y la variable
a la cual asociar el valor. Este mecanismo se utiliza
en el siguiente ejemplo para asociar el primer elemento de la lista en l para mostrarlo, y al mismo
tiempo asociar la lista completa a ls para calcular
su longitud:
which n
| n == 0 = "Cero"
| even n = "Par"
| otherwise = "Impar"
Note el otherwise siempre evala verdadero y
puede ser utilizado para especificar un caso por
default.
Las guardas se pueden utilizar con patrones.
El siguiente ejemplo es una funcin que determina
si el primer caracter en una cadena es mayscula
o minscula:
3
allEmpty _ = False
allEmpty [] = True
alwaysEven n
| otherwise = False
| n `div` 2 == 0 = True
Sintaxis de Registros
Normalmente la comparacin de patrones ocurre basndose en la posicin en los argumentos del valor a coincidir. Los
tipos que se declaran con sintaxis de registro, sin
embargo, pueden coincidir basndose en los nombres en el registro. Dado el siguiente tipo de datos:
jgbailey@codeslower.com
ejemplo, podemos definir un tipo Pixel y una funcin que reemplace con negro valores con componente green diferente de cero:
pero el constructor puede ser Nothing. La siguiente definicin podra funcionar, pero no es ptima porque obtenemos Nothing cuando se le pasa
Nothing.
una lista de valores objetivo basados en los generadores y en las guardas proporcionados. Esta
comprensin genera todos los cuadrados:
strange = [(a,z) |
,
,
,
,
a <-[1..3]
b <-[1..3]
c <- [1..3]
let z = min a b
z < c ]
ups =
[c | c <- [minBound .. maxBound]
, isUpper c]
jgbailey@codeslower.com
idxs palabras br =
[i | (i, c) <- zip [0..] palabras
, c == br]
Un aspecto nico de las listas por comprensin es
que los errores en la comparacin de patrones no
causan un error; simplemente son omitidos de la
lista resultante.
Operadores
Hay muy pocos operadores predefinidos en
Haskellmuchos que parecen estar predefinidos
en realidad son sintaxis (e.g. =). En lugar de
eso, los operadores son simplemente funciones
que toman dos argumentos y tienen un soporte
sintctico especial. Cualquier as llamado operador puede ser aplicado como una funcin prefijo
usando parntesis:
3 + 4 == (+) 3 4
Para definir un nuevo operador, simplemente defnalo como una funcin normal, excepto que el
operador aparezca entre los dos argumentos. Este
es uno que inserta una coma entre dos cadenas y
asegura que no aparezcan espacios adicionales:
first ## last =
let trim s = dropWhile isSpace
(reverse (dropWhile isSpace
(reverse s)))
in trim last ++ ", " ++ trim first
c 2009 Justin Bailey.
infixl 8 `plus1`
plus1 a b = a + b
infixl 7 `mult1`
mult1 a b = a * b
> 2 + 3 * 5
17
> 2 `plus1` 3 `mult1` 5
25
Revertir la asociatividad tambin tiene efectos interesantes. Redefiniendo la divisin como asociativa por la derecha:
infixr 7 `div1`
div1 a b = a / b
> 20 / 2 / 2
5.0
> 20 `div1` 2 `div1` 2
20.0
jgbailey@codeslower.com
Aplicacin parcial
En Haskell las funciones no tienen que recibir todos sus argumentos de una vez. Por ejemplo, considere la funcin convertOnly, que solamente convierte ciertos elementos de una cadena dependiendo de una prueba:
toma una cadena y produce una cadena. l33t regresa una versin currificada de convertOnly,
donde solamente dos de sus tres argumentos han
sido provistos.
Esto puede ser llevado ms lejos. Digamos
que queremos escribir una funcin que solamente
cambie letras maysculas. Sabemos cual es la
prueba a aplicar, isUpper, pero no queremos especificar la conversin. Esa funcin puede ser escrita como:
add10 = (+) 10
Sin embargo esto es incmodo y difcil de leer. Las
secciones son operadores currificados, usando
parntesis. Este es add10 usando secciones:
6
add10 = (10 +)
El argumento provisto puede estar del lado
izquierdo o derecho, lo que indica qu posicin
debe tomar. Esto es importante para operaciones
como la concatenacin:
noGreen1 (C r _ b) = C r 0 b
Esto es algo extenso y puede ser vuelto a escribir
con sintaxis de registro. Este tipo de actualizacin solamente establece valores para los campos especificados y copia los dems:
noGreen2 c = c { green = 0 }
jgbailey@codeslower.com
Aqu capturamos el valor Color en c y devolvemos un nuevo valor Color. Ese valor resulta tener
el mismo valor para red y blue que c y su componente green es 0. Podemos combinar esto con
comparacin de patrones para establecer los campos green y blue para que sean iguales al campo
red:
Funciones Annimas
Una funcin annima (i.e., una expresin lambda o
simplemente lambda) es una funcin sin nombre.
Pueden ser definidas en cualquier momento de la
siguiente forma:
\c -> (c, c)
que define una funcin que toma un argumento
y regresa un tuple conteniendo ese argumento en
ambas posiciones. stas son tiles para funciones
simples donde no necesitamos un nombre. El
ejemplo siguiente determina si una cadena consiste solamente de letras maysculas o minsculas
y espacio en blanco.
mixedCase str =
all (\c -> isSpace c ||
isLower c ||
isUpper c) str
c 2009 Justin Bailey.
multBy n = \m -> n * m
Por ejemplo:
Declaracin de tipos
Haskell cuenta con inferencia de tipos, lo que significa que casi nunca es necesario declarar los
tipos. Indicarlos es todava til al menos por dos
razones.
DocumentacinAn si el compilador puede inferir los tipos de sus funciones, otros programadore o an usted mismo podra no ser capaz de hacerlo ms tarde. Declarar los tipos
en todas las funciones del nivel principal se
considera una buena prctica.
EspecializacinLas clases de tipos permiten sobrecargar funciones. Por ejemplo, una funcin que hace la negacin de una lista de
nmeros tiene la declaracin de tipos:
Unidad
() tipo unidad y valor unidad. El valor y
tipo que no representa informacin til.
Palabras Clave
Las palabras clave en Haskell estn listadas a continuacin, en orden alfabtico.
Case
case es similar a la declaracin switch en C# o
Java, pero puede hacer comparacin de un patrn:
la forma del valor siendo inspeccionado. Considere un tipo de datos simple.
data Choices = First String | Second |
Third | Fourth
case puede ser utilizado para determinar qu opcin se seleccion:
whichChoice ch =
case ch of
First _ -> "1st!"
Second -> "2nd!"
_ -> "Something else."
c 2009 Justin Bailey.
anyChoice1 ch =
case ch of
Nothing -> "No choice!"
Just (First _) -> "First!"
Just Second -> "Second!"
_ -> "Something else."
Se puede asociar un nombre al valor comparado
para poder manipularlo:
anyChoice2 ch =
case ch of
Nothing -> "No choice!"
Just score@(First "gold") ->
"First with gold!"
Just score@(First _) ->
"First with something else: "
++ show score
_ -> "Not first."
8
Orden de Comparacin
La comparacin procede de arriba hacia abajo.
Si el orden de
anyChoice se modifica de la siguiente forma, el
primer patrn siempre tendr xito:
anyChoice3 ch =
case ch of
_ -> "Something else."
Nothing -> "No choice!"
Just (First _) -> "First!"
Just Second -> "Second!"
Guardas
Las guardas, o comparaciones condicionales, se pueden utilizar en casos igual que en
la definicin de funciones. La nica diferencia es
el uso de -> en lugar de =. Esta es una funcin
que hace comparacin de cadenas sin importar si
las letras son mayscula o minscula:
Clases
Una funcin en Haskell es definida para funcionar
con un cierto tipo o conjunto de tipos y no puede
ser definida ms de una vez. Muchos lenguajes cuentan con el concepto de sobrecarga,
donde una funcin puede tener diferente comportamiento dependiendo del tipo de sus argumentos. Haskell implementa sobrecarga a travs
de declaraciones de clase y de instancia. Una
jgbailey@codeslower.com
Defaults
Se pueden dar implementaciones por
default para las funciones en una clase. stas
son tiles cuando ciertas funciones pueden ser
definidas en trminos de otras en la clase. Un default es definido dadno un cuerpo a una de las
funciones miembro. El ejemplo cannico es Eq,
que define /= (no igual) en trminos de ==. :
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
(/=) a b = not (a == b)
Se pueden crear definiciones recursivas. Continuando con el ejemplo de Eq, == puede ser definido
entrminos de /=:
(==) a b = not (a /= b)
Sin embargo, si las instancias no proveen implementaciones suficientemente concretas de las funciones miembro, el programa que use esas instancias puede entrar en ciclo infinito.
Data
Los tipos de datos algebraicos pueden ser declarados
de la siguiente forma:
jgbailey@codeslower.com
Variables de Tipo
Declarar tipos de datos
polimrficos es tan fcil como agregar variables de
tipo en la declaracin:
Restricciones de Clase
Se pueden declarar
tipos de datos con restricciones de clase en las
variables de tipo, pero en general esta prctica es
desaprobada. Generalmente es mejor ocultar los
constructores de datos empleando el sistema de
mdulos y exportar constructores inteligentes
que apliquen las restricciones apropiadas. En
cualquier caso, la sintaxis es:
Deriving
Vea la seccin en deriving bajo la palabra clave
data en la pgina 10.
Do
La palabra clave do indica que el cdigo a continuacin estar en un contexto mondico. Las
declaraciones estn separadas por saltos de lnea,
la asignacin es indicada por <-, y se puede emplear una forma de let que no requiere la palabra
clave in.
jgbailey@codeslower.com
If con IO
if puede ser complicado cuando se
utiliza con IO. Conceptualmente no es diferente
de un if en cualquier otro contexto, pero intuitivamente es difcil de asimilar. Considere la funcin doesFileExists de System.Directory:
wrong fileName =
if doesFileExist fileName
then ...
else ...
Eso es, doesFileExost resulta en un valor
IO Bool, mientras que if quiere un valor Bool.
El valor correcto debe ser extrado ejecutando la
accin IO:
right1 fileName = do
exists <- doesFileExist fileName
if exists
then return 1
else return 0
Note el uso de return. Como do nos coloca dentro de la mnada IO, no podemos salirexcepto
a travs de return. Ntese que no tenemos
que usar if directamente aqutambin podemos
usar let para evaluar la condicin y obtener un
resultado primero:
c 2009 Justin Bailey.
right2 fileName = do
exists <- doesFileExist fileName
let result =
if exists
then 1
else 0
return result
Una vez ms, notar donde est return. No lo
ponemos en la declaracin let. En lugar de eso
lo usamos una vez al final de la funcin.
dos Multiples
Al usar do con if o case, se requiere otro do is cualquier rama tiene mltiples
declaraciones. Un ejemplo con if:
countBytes1 f =
do
putStrLn "Enter a filename."
args <- getLine
if length args == 0
-- no 'do'.
then putStrLn "No filename given."
else
-- multiple statements require
-- a new 'do'.
do
f <- readFile args
putStrLn ("The file is " ++
show (length f)
++ " bytes long.")
Y uno con case:
countBytes2 =
do
putStrLn "Enter a filename."
args <- getLine
11
case args of
[] -> putStrLn "No args given."
file -> do
f <- readFile file
putStrLn ("The file is " ++
show (length f)
++ " bytes long.")
Una sintaxis alternativa usa llaves y punto y coma.
Todava se requiere un do, pero la sangra es innecesaria. Este cdigo muestra un ejemplo de
case, pero el principio aplica igual con if:
countBytes3 =
do
putStrLn "Enter a filename."
args <- getLine
case args of
[] -> putStrLn "No args given."
file -> do { f <- readFile file;
putStrLn ("The file is " ++
show (length f)
++ " bytes long."); }
Export
Vea la seccin module en la pgina 12.
jgbailey@codeslower.com
Import
Ver la seccin module en la pgina 12.
In
Ver let en la pgina 12.
Instance
Ver la seccin class en la pgina 8.
Let
Se pueden definir funciones localmente dentro de
una funcin usando let. La palabra clave let
debe siempre ser seguida por in. El in debe aparecer en la misma columna que la palabra clave let.
Las funciones definidas tienen acceso a todas las
dems funciones y variables dentro del mismo
contexto (incluyendo las definidas por let). En
este ejemplo, mult multiplica su argumento n por
x, que fue recibido del multiples original. mult
c 2009 Justin Bailey.
onlyThree str =
let (a:b:c:[]) = str
in "The characters given are: " ++
show a ++ ", " ++ show b ++
", and " ++ show c
multiples x =
let mult n = n * x
in map mult [1..10]
Las funciones let sin argumentos son en realidad constantes y, una vez que son evaluadas, no
sern evaluadas otra vez. Esto es til para capturar porciones comunes de su funcin y reutilizarlas. El siguiente es un ejemplo que da la suma
de una lista de nmeros, su promedio, y su mediana:
listStats m =
let numbers = [1,3 .. m]
total = sum numbers
mid = head (drop (m `div` 2)
numbers)
in "total: " ++ show total ++
", mid: " ++ show mid
Deconstruccin
El lado izquierdo de una
definicin let puede tambin desestructurar su
argumento, en caso de que se requiera acceso a
sus sub-componentes. Esta definicin extraer los
primeros tres caracteres de una cadena:
firstThree str =
let (a:b:c:_) = str
in "Initial three characters are: " ++
show a ++ ", " ++
show b ++ ", and " ++
show c
Note que esto es diferente de lo que sigue, que
solamente funciona si la cadena tiene exactamente
tres caracteres:
12
Of
Vea la seccin case en la pgina 8.
Module
Un mdulo es una unidad de compilacin que exporta funciones, tipos, clases, instancias, y otros
mdulos. Un mdulo solamente se puede definir
en un solo archivo, aunque lo que exporte puede
provenir de varias fuentes. Para convertir un
archivo Haskell en mdulo basta con agregar una
declaracin de mdulo hasta arriba:
jgbailey@codeslower.com
Import
Las bibliotecas estndar Haskell estn
divididas en un nmero de mdulos. Se accede a
la funcionalidad provista por esas bibliotecas importndolas en el programa fuente. Para importar
todo lo que exporta una biblioteca, simplemente
use el nombre del mdulo:
import Text.Read
Todo significa todo: funciones, tipos de datos y
constructores, declaraciones de clase, y an otros
mdulos importados y luego exportados por ese
mdulo. Para importar selectivamente se pasa
una lista de nombres qu importar. Pr ejemplo,
aqu importamos algunas funciones de Text.Read:
tipos
clases
En el caso de las clases, podemos importar las funciones definidas para una clase usando una sintaxis similar a importar constructores para tipos
de datos:
para mdulos que tienen un gran nmero de funciones con el mismo nombre que funciones del
Prelude. Data.Set es un buen ejemplo:
removeDups a =
Set.toList (Set.fromList a)
allUpper str =
all Char.isUpper str
Excepto por el prefijo especificado, la importacin calificada emplea la misma sintaxis que una importacin
normal. Los nombres importados se pueden limitar de
las mismas formas descritas arriba.
jgbailey@codeslower.com
Export
Si no se provee una lista de exportaciones,
entonces todas las funciones, tipos, constructores, etc.
estarn disponibles siempre que se importe el mdulo.
Note que los mdulos importados no son exportados
en este caso. Limitar los nombres exportados se consigue agregando una lista entre parntesis de los nombres antes de la palabra clave where:
Newtype
Mientras que data agrega nuevos valores y type
solamente crea sinnimos, newtype est en un
punto medio. La sintaxis para newtype est ms
restringidasolamente se puede definir un constructor, y ese constructor solamente puede tomar un argumento. Continuando con el ejemplo de arriba, podemos
definir un tipo Phone de la forma que sigue:
El verificador de tipos puede hacer que tipos comunes como Int o String se usen de formas restringidas, especificadas por el programador.
Finalmente, se debe notar que cualquier clusula
deriving que puede ser anexada a una declaracin
data puede tambin ser usada al declarar un newtype.
Return
Ver do en la pgina 10.
Type
Esta palabra clave define un sinnimo de tipo (i.e.,
alias). Esta palabra clave no define un tipo nuevo, como
data o newtype. Es til para documentar cdigo pero,
adems de eso, no tiene efecto en el tipo de una funcin
o valor dados. Por ejemplo, un tipo de datos Person
puede ser definido como:
lName (Person f l ) =
Person (lower f) (lower l)
Como type es solamente un sinnimo, no puede
declarar mltiples constructores de la forma que data
puede. Se pueden usar variables de tipo, pero no puede
haber ms que las las variables de tipo declaradas con
el tipo original. Eso significa que un sinnimo como el
siguiente es vlido:
Where
De la misma forma que let, where define funciones y
constantes locales. El contexto de una definicin where
strlen [] = result
where result = "No string given!"
strlen f = result ++ " characters long!"
where result = show (length f)
Where vs. Let
Una clusula where solamente
puede ser definida al nivel de una definicin de funcin. Usualmente, eso es idntico al contexto de una
definicin let. La nica diferencia es en cundo son
utilizadas las guardas. En contraste, el contexto de una
expresin let es solamente la clusula actual de la funcin y la guarda, de existir.
Traduccin al Espaol
Jaime Soffer, jaime.soffer@gmail.com
15
Contributors
My thanks to those who contributed patches and useful suggestions: Dave Bayer, Paul Butler, Elisa Firth,
Marc Fontaine, Cale Gibbard, Stephen Hicks, Kurt
Hutchinson, Johan Kiviniemi, Adrian Neumann, Barak
Pearlmutter, Lanny Ripple, Markus Roberts, Holger
Siegel, Adam Vogt, Leif Warner, and Jeff Zaroyko.
Version
This is version 2.0. The source can be found at GitHub
(http: // github. com/ m4dc4p/ cheatsheet ). The
latest released version of the PDF can be downloaded
from
http: // cheatsheet. codeslower. com .
Visit CodeSlower.com (http: // blog. codeslower.
com/ ) for other projects and writings.
Traduccin al Espaol version 0.01 sobre la versin original mencionada arriba.
Repositorio en
http://github.com/jsoffer/cheatsheet
jgbailey@codeslower.com