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:

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