Haskell

* Basado en el estudio de: Haskell for Good Great!,  Haskell.org  & Real World of Haskell

Haskell es un lenguaje de programación funcional… qué rayos significa esto?, pues bien… los lenguajes de programación son diseñados de acuerdo a cierta estructura… si Ud es programador o al menos ha tenido alguna experiencia de hacer un helloworld, suma de números o codear el proceso de encuestas en programas tradicionales como C o en PHP, entonces se habrá dado cuenta que se establece una secuencia de comandos o sentencias una debajo de otra -> primero leer, luego calcular, finalmente imprimir resultado; en este proceso, una variable “x” se puede setear con cierto valor y luego procesarlo para que cambie de valor, además en este tipo de paradigma de programación se hace uso de estructuras iterativas como los bucles para que se puedan trabajar cierto proceso cuantas veces se indique. En este caso, Ud ha pogramado bajo el paradigma de programación imperativo.

Con el paradigma funcional ocurren cosas diferentes, no se puede dar un valor a una variable Z y luego hacer procesamientos para que cambie de valor, aquí no se permite alterar los valores, con ello se evitan side effects, por tanto los bugs. Tampoco se dan sentencias o comandos, en este caso se define qué es lo que hay que hacer y se define la tarea a través de funciones. Se puede reusar la función de manera recursiva y se establece valores condicionales pero no se usan estructuras loop, aunque parece algo trágico dejar las costumbres de la programación imperativa, en mi experiencia con Haskell al igual de la de LIPS, se obtienen códigos más pequeños y concisos que pueden ser deducidos fácilmente (:

Antes de todo… ¿ Cómo cargamos el prompt de Haskell? 

Es muy sencillo desde linux, sólo abrimos el terminal y ejecutamos el comando ghci, en seguida carga el prompt Prelude>, si queremos que nos aparezca el prompt ghci>, sólo escribiremos :set prompt “ghci>” y listo!

Características que hereda Haskell de la programación funcional

Las siguientes características están basados en las lecturas [O’Sullivan 98],[Lipovača 11]:

  • Haskell emplea características de un lenguaje funcional como la evaluación perezosa, la cual realiza operaciones cuando se le indique, no antes, ni después. Un ejemplo para demostrar este proceso es:

ghci> True||( (a0+a1z+a2z2+…) (b0+b1z+b2z2+…)=a0b0+ a0(b1z+b2z2+…)+ (a1z+a2z2+…) (b0+b1z+b2z2+…) )

ghci> True

La estructura interna de Haskell conoce que en una operación booleana ‘||’ arroja resultado “False”, si ambas premisas son “False”, caso contrario el resultado será “True”. En este caso la primera proposición es True por lo tanto ya no es necesario evaluar la segunda proposición, por lo tanto el resultado es “True” y no evalúa operaciones que no son necesarias.

  • Haskell emplea un sistema de tipo de datos estático, que hace que los errores sean detectados en tiempo de compilación, si evalúa la operación entre un valor de tipo numérico con uno booleano (True && 1), nos arrojará un mensaje de error.

Además cuenta con un sistema de inferencia de datos, el cual permite no declarar los tipos, es así que si colocamos a= 3+4, Haskell resuelve que “a” es de tipo numérico.

  • Utiliza tipo amplia gama de tipos integrados (desde caracteres hasta números grandes), listas de comprensión, definición de nuevos tipos de datos y sinónimos de tipos. Ejemplo: los tipos de datos como Bool, Int son definidos de la siguiente manera:

data Bool = False | True                                                          – – False y True son de un tipo de dato Bool

data Int = -2147483648  | -2147483647  | … | -1 | 0 | 1 | 2 |… | 2147483647      – – intervalo de tipo Int

Otro ejemplo para definir nuevos tipos de datos es el de un punto de dos dimensiones:

data Point = Point Float Float deriving (Show)             – – Float para coordenada ‘x’ y otro Float para ‘y’

Un ejemplo que puede ser relacionado con las estructuras ‘struct’ en C y que define un tipo de dato ‘Persona’ (los tipos se escriben con la primera letra en mayúscula) es:

data Person = Person { firstName :: String
, lastName :: String
, age :: Int
, height :: Float
, phoneNumber :: String
, flavor :: String
} deriving (Show)

* En un terminal Haskell, el comando ‘:t’ indica el tipo de dato ya definido:

ghci> :t flavor

flavor :: Person -> String

ghci> :t firstName

firstName :: Person -> String

Haskell define a ‘Nothing’ y ‘Just’ como valores del tipo de dato ‘Maybe’

data Maybe a = Nothing | Just a     - - ‘a’ es un ‘parámetro tipo’, puede ser de tipo Int, String, Persona, etc

* En un terminal Haskell, veamos el comportamiento de ‘Nothing’:

ghci> :t Nothing                            - - primera consulta: cuál es el tipo de ‘Nothing’
Nothing :: Maybe a
ghci> Nothing < Just 100              - - segunda consulta: comparando el valor de ‘Nothing’
True

–  En la primera consulta se indica que ‘Nothing’ es de tipo ‘Maybe a’.

‘a’ es definido en Haskell como un tipo variable, que quiere decir que puede ser de cualquier tipo, esto es ideal cuando se emplean funciones polimórficas.

Ejemplo: Uso de la función ‘fst’ en listas de enteros, decimales y cadenas:

– – Haskell soporta sobrecarga de tipo de clase a través de funciones polimórficas.

ghci>fst(3,61)
3
ghci>fst(3.4,5.8)
3.4
ghci>fst('k','y')
'k'

* En un terminal Haskell, veamos el comportamiento de ‘Just’:

ghci> :t Just "Tesis"                        - - primera consulta: cuál es el tipo de ‘Just “Tesis”’
Just "Tesis" :: Maybe [Char]
ghci> :t Just 84                              - - segunda consulta: cuál es el tipo de ‘Just 84’
Just 84 :: (Num t) => Maybe t
ghci> Just 10 :: Maybe Double    - - asignando un tipo Maybe Double a Just 10
Just 10.0

–  En la primera consulta, se obtiene que ‘Just Tesis’ es de tipo ‘Maybe [Char] ’.

El sistema de tipos de Haskell infiere el tipo de la variable consultada.

‘Just’ es apropiado en lugar de Nothing (un análogo de ‘NULL’).

–  En la segunda consulta, se obtiene que ‘Just 84’ es de tipo ‘Maybe t ’ que a su vez, pertenece a ‘Num t’.

El sistema de tipos de Haskell define typeclasses, que son conjuntos de tipos de datos. Se tiene así que el typeclass ‘Num’ está conformado por otros dos sub-typeclasses: ‘Integral’ y ‘Floating’. El typeclass ‘Integral’, incluye a los tipos ‘Int’ y ‘Integer’; y  el typeclass ‘Floating’, incluye los tipos ‘Float’ y ‘Double’.

–  En el tercer escenario se asigna el tipo ‘Maybe Double’ a ‘Just 10’.

Esto es posible porque el sistema de tipos de Haskell permite interacción entre los tipos Int y Double a través del typeclass ‘Num’. Un ejemplo ilustrativo de ello es la suma de una variable tipo ‘Int’ con otra variable tipo ‘Float’:

ghci>5+4.8    - - no es necesario declarar los tipos de variables pero se puede consultar tipo con ‘:t’
9.8
  • Haskell utiliza funciones de orden superior, lo que significa que la salida de una función puede ser entrada o parámetro que puede ser utilizado por otra función.

Ejemplo: cálculo de la suma de los cuadrados de números pares menores que 10000.

sum (takeWhile (<10000) (filter odd (map (^2) [1..])))    - - ‘sum’, ‘takeWhile’, ‘filter’, ‘odd’ y ‘map’ son funciones

Haskell también permite composición de funciones:

ghci>map (negate . sum . tail) [[4,7,8],[4,5,6]]      - -  ‘map’, ‘negate’, ‘sum’ y ‘tail’ son funciones predeterminadas
[-15,-11]

–  En ambos ejemplos se utilizan funciones predeterminadas, quienes según la teoría de Haskell Curry toman un parámetro por cada función. Lo que internamente se procesa cuando se tiene más de un parámetro es el resultado, el que luego es aplicado parcialmente a otro parámetro y así sucesivamente.

–  La función sum, suma los elementos de una lista. Ejemplo: ghci>sum [1,2,4]   – – Rpta: 7

–  La función takeWhile, toma los elementos según se condicione. Ejemplo:

ghci>takeWhile (>2) [3,4,1,2]     - - Rpta:[3,4]

–  La función filter, filtra elementos según se condicione. Ejemplo:

ghci>filter (odd) [4,6,7,8,9]         - - Rpta:[7,9]

–  La función odd, verifica números impares. Ejemplo:

ghci>odd 13                               - - Rpta: True

–  La función map, permite agrupar más de una función. Ejemplo:

         ghci>map (+ 1) [1,2,3]                - - Rpta: [2,3,4]

–  La función negate, aplica el negativo de un número. Ejemplo:

ghci>negate (-6)                         - - Rpta:  6

–  La función tail, retorna la lista sin el primer elemento. Ejemplo:

ghci>tail [4,5,6,7,0]                     - - Rpta: [5,6,7,0]
  • Haskell hace uso del patrón sintáctico que incluye asignación de expresiones, condicionales ‘if-else’, ‘case’, uso de ‘guards’, notación ‘do’ y patrones ‘as’.

Un primer ejemplo ilustrativo de patrón sintáctico es el siguiente.

chapter :: (Integral a) => a -> String
chapter 1 = "Estas leyendo el capítulo 1"
chapter 2 = " Estas leyendo el capítulo 2"
chapter 3 = " Estas leyendo el capítulo 3"
chapter 4 = " Estas leyendo el capítulo 4"
chapter 5 = " Estas leyendo el capítulo 5"
chapter x = "No existe este capítulo en esta tesis”

Un ejemplo aplicable con connotación matemática/física es:

addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)

addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)

Un ejemplo de uso de la palabra reservada ‘let’ es:

ghci> [let square x = x * x in (square 5, square 3, square 2)]       - - square: funcion que eleva al cuadrado
[(25,9,4)]

Un ejemplo de uso de la palabra reservada ‘ if – else’

ghci> 4 * (if 10 > 5 then 10 else 0) + 2
42

Un ejemplo de uso de la palabra reservada ‘case’

describeLista :: [a] -> String                               - - se define los tipos de entrada y salida en la función

describeLista xs = “Esta lista esta ” ++ case xs of [ ] -> “vacia.”

[x] -> “conformada por un elemento.”

xs ->  “conformada por muchos elementos.”

Ejemplos de uso de los ‘guards’ son:

contador :: Int -> String
contador n
|n == 0 = "no hay elementos"
|n == 1 = "1 elemento"
|n >  1 = show n ++ " elementos"

myCompare :: (Ord a) => a -> a -> Ordering
a `myCompare` b
| a > b     = GT
| a == b    = EQ
| otherwise = LT

Un ejemplo de uso de la notación ‘do’ es:

foo :: Maybe String
foo = do
x <- Just 3
y <- Just "!"
Just (show x ++ y)

Un ejemplo de uso de la notación ‘as’ es:

capital :: String -> String
capital "" = "cadena vacia"
capital all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]
* ‘all’ es el alias para (x:xs), es así que si corremos el programa se tiene:

ghci> capital “Dracula”

“The first letter of Dracula is D”

  • Haskell hace uso de la recursión para tratar datos que se trataban con estructuras “loops o bucles” en los lenguajes tradicionales como C.

Ejemplo:

replicar :: (Num i, Ord i) => i -> a -> [a]
replicar n x
| n <= 0   =  [ ]
| otherwise = x : replicar (n-1)  x


  •  En Haskell se utilizan ‘Modules’, esto es un análogo de las librerías que se utilizan en los lenguajes imperativos.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s