En este primer articulo sobre programacion funcional vamos a ver la parte que se refiere al funtor
Hace algunos meses escribi 3 articulos en el blog de Paradigma Digital sobre Programacion Funcional:
- Qué es la programación funcional y por qué deberías usarla
- Pásate a la programación funcional y olvida los NullPointerException
- Optimización de procesos con Programación Funcional
Aunque estos articulos dan una introduccion a algunos conceptos de la programacion funcional hay otros que se quedan fuera y que son muy interesantes. Recientemente he hecho varios cursos sobre programacion funcional en Hablapps y he aprendido muchas cosas que se pueden aplicar a la programacion para crecer profesionalmente y mejorar nuestros desarrollos
Un poco de teoria
Comencemos con un poco de teoria, en concreto, teoria de categorias. Todos los conceptos que vamos a ver son conceptos matematicos y ya que la informatica esta basada en conceptos matematicos todos estos se pueden aplicar
Una categoria, dicho de una forma simple, es una forma de representar cosas y de ir entre una cosa a otra cosa. Para una definicion mas amplia siempre podemos echar un vistazo en la wikipedia
Con lo que tenemos que quedarnos es con que una categoria tiene los siguientes elementos:
- Objetos
- Morfismos
- Una ley de composicion
Intentare explicarlo de una forma simple. Si tenemos una categoria con 3 objetos, A
, B
y C
donde tenemos un morfismo de A
a B
llamado f
y un morfismo de B
a C
llamado g
podemos componer f
y g
para obtener un morfismo de A
a C
Las categorias pueden ser de cualquier cosa, Strings, numeros, listas, gatos…
Funtor
Visto esto, llegamos a la primera cosa que nos interesa, los funtores. Basicamente un funtor asocia objetos entre categorias
Kind
En Java tenemos tipos de objetos: String
, Integer
, List
, Map
, … Algunos de estos tipos pueden tener un kind que es un tipo de un tipo. Por ejemplo, la clase List
puede tener un kind String
y se representa con List<String>
Podemos crear un funtor que convierta de un tipo a otro de esta manera
class ListFunctor { List<String> map(List<Integer> fa, Function<Integer, String> f) { return fa.stream() .map(f) .collect(Collectors.toList()); }
Podemos hacer esta clase mas generica de la siguiente manera
class ListFunctor<A, B> { List<B> map(List<A> fa, Function<A, B> f) { return fa.stream() .map(f) .collect(Collectors.toList()); } }
Ahora esta clase nos vale para cualquier List
con cualquier kind. Pero y si queremos hacer lo mismo para cualquier tipo, ya sea List, ArrayList, Map, …?
Veamos como seria nuestra clase, o mejor dicho, nuestra interfaz
public interface Functor<T> { <A, B> T<B> map(T<A> fa, Function<A, B> f); }
Lamentablemente esto en Java no se puede hacer. Por suerte hay una libreria que nos permite hacer esto que se llama hkt y nuestro codigo quedaria asi
public interface Functor<f> { <A, B> __<f, B> map(__<f, A> fa, Function<A, B> f); }
El unico problema es que no podemos usar las clases originales de Java y tenemos que hacer un pequeño wrapper para poder utilizarlas
@HktConfig(withVisibility = Visibility.Same) public class ListT<t> implements __<ListT, t> { public final List<t> list; public ListT(List<t> list) { this.list = list; } }
Una vez hecho esto ya podemos implementar nuestro funtor
class ListFunctor implements Functor<ListT> { @Override public <A, B> __<ListT, B> map(__<ListT, A> fa, Function<A, B> f) { List<B> listb = Hkt.asListT(fa).list.stream() .map(f) .collect(Collectors.toList()); return new ListT<>(listb); } }
Con esto podemos hacer poco por el momento pero segun avancen los articulos veremos como usarlo en nuestros desarrollos