lunes, 8 de diciembre de 2014

Implementar booleanos de Javascript con Haskell


En javascript muchos tipos se comportan como boolenos, por ejemplo el string vacío “” o el cero se comportan como el false.

Nosotros podemos tener el mismo comportamiento en Haskell si lo deseamos, esto gracias a type class o clases de tipos.

Las clases de tipos son una especie de interfaz que define algún tipo de comportamiento. Si un tipo es miembro de una clase de tipos, significa que ese tipo soporta e implementa el comportamiento que define la clase de tipos. La gente que viene de lenguajes orientados a objetos es propensa a confundir las clases de tipos porque piensan que son como las clases en los lenguajes orientados a objetos. Bien, pues no lo son. Una aproximación más adecuada sería pensar que son como las interfaces de Java, o los protocolos de Objective-C, pero mejor.

¿Cuál es la declaración de tipo de la función ==?

ghci> :t (==)
(==) :: (Eq a) => a -> a -> Bool

Interesante. Aquí vemos algo nuevo, el símbolo =>. Cualquier cosa antes del símbolo => es una restricción de clase. Podemos leer la declaración de tipo anterior como: la función de igualdad toma dos parámetros que son del mismo tipo y devuelve un Bool. El tipo de estos dos parámetros debe ser miembro de la clase Eq (esto es la restricción de clase).

La clase de tipos Eq proporciona una interfaz para las comparaciones de igualdad. Cualquier tipo que tenga sentido comparar dos valores de ese tipo por igualdad debe ser miembro de la clase Eq. Todos los tipos estándar de Haskell excepto el tipo IO (un tipo para manejar la entrada/salida) y las funciones forman parte de la clase Eq.

Muy bien! Ahora vamos a intentar implementar el comportamiento de el tipo booleano en diferentes tipos, se nos va hacer bastante sencillo utilizando clase de tipos.

Lo que debemos hacer es crear una clase, que la definimos de la siguiente manera:

class YesNo a where
        yesno:: a -> Bool

Muy simple! YesNo es un typeclass que define una función. Esta función toma un argumento y devuelve true cuando se defina. Pero lo importante de la definición es que a no tiene tipo. Cuando realicemos instancias de esta función vamos a definir este tipo.

Instance YesNo Int where
     yesno 0 = False
     yesno _ = True

De esta manera indicamos como los enteros implementan nuestra clase. De esta manera cuando un entero es 0 devuelve false sino devuelve true.

Instance YesNo [a] where
     yesno [] = False
     yesno _ = True

También podemos definir el comportamiento para listas. Cuando la lista es vacía devuelve false sino true. Note que no definimos el tipo de la lista tampoco.

Instance YesNo Bool where
     yesno = id

En este caso estamos acudiendo a una función de la librería estándar "id", que toma el parámetro y lo retorna.
Podemos definir esta función para cualquier tipo que se nos ocurra...

Y luego podemos probarla:

> yesno “hola”
True
> yesno “”
False
> yesno 0
False

Muy simple!!