Thursday, February 8, 2007

Diversión con functores

Despues de programar durante algún tiempo, es común encontrarse repitiendo una y otra vez las mismas soluciones, con ligeras variantes. Es esta ha originado todas las innovaciones en el campo, desde la creación de subrutinas hasta los macros.

Una de estas situaciones se presenta cuando se tiene una funcion que convierte datos del tipo A al tipo B y se la desea transformar en una nueva función que opere sobre un tipo derivado de A para convertirlo en un tipo derivado (en una manera equivalente) de B.

La definición de functores es:

Dadas dos categorías C y D, un functor F de C a D es un mapeo que:
  • Asocia a cada elemento X de C un elemento F(X) de D
  • Asocia a cada funcion f: X -> Y de C otra F(f): F(X) -> F(Y)
De manera tal que
  • F(IDx) = ID(Fx)
  • F(f o g) = F(f) o F(g)
Desde el punto de vista programatico, un functor esta formado por dos funciones: la primera convierte tipos "simple" en tipos "complejos" y la segunda convierte funciones cuyo dominio y rango son tipos "simples" a otras funciones, en algún sentido equivalentes, pero cuyo dominio y rango son tipos "complejos". De esta forma, si has programado una función que convierta manzanas en compotas, aplicando un functor tienes automaticamente una funcion que convierta cajas de manzanas en cajas de compotas o camiones de manzanas en camiones de compotas.

Un ejemplo practico es el functor cuyo rango son las listas. La categoría C sería el conjunto de todos los tipos que son susceptibles de ser contenidos dentro de listas (enteros, reales, complejos, caracteres, incluso listas, ...). La categoría D esta formada por las listas de los tipos en la categoría C (listas de enteros, listas de reales, listas de complejos, listas de caracteres, listas de listas, ...).

Para definir dicho functor, necesitamos una función que convierta elementos de los tipos en C a elementos en los tipos en D. En este caso la función es simple, solo necesita crear una lista con ese unico elemeto. El otro elemento necesario es una función que convierta una funcion del tipo X -> Y en una funcion del tipo [X] -> [Y]. En Haskell esta función se denomina map, y su definición es:

map [] f = []
map x:xs = (f x) : (map xs)


Eso es todo. Al definir map, se ha solucionado de una vez por todas el problema de mapear funciones hacia la categoría de las listas. Desde luego, cada categoría distinta requiere un mapeo distinto. Normalmente la primera parte del mapeo es el constructor de tipos y la segunda recibe como nombre genérico "fmap".

La belleza de la programación funcional en lenguajes como Haskell es la simplicidad con la que se implementan conceptos como los functores. Lengajes imperativos como Java poseen todo lo necesario para crear functores: funciones que mapean tipos a otros tipos (generics en el lenguaje de java), funciones como ciudadanos de primera clase (en java, clases) e interfaces. Sin embargo, su implementación es mucho mas verbosa.

No comments: