monada

Seguimos introduciendo conceptos funcionales para javeros. Despues de la primera entrega sobre funtores y la segunda sobre funtores aplicativos nos toca ver la monada

Pero que es una monada? Segun la Wikipedia, una monada es una estructura que representa cálculos definidos como una secuencia de pasos. Es decir que podemos usar monadas para crear programas definiendo los pasos que hacen que nuestro programa funcione

Clase Monada

Lo primero que tenemos que hacer es definir nuestra clase monada. Una monada es un funtor aplicativo asi que tenemos que extender de nuestra interfaz Applicative. Ademas de extender esa interfaz tenemos que añadirle 1 metodo llamado bind

public interface Monad<f> extends Applicative<f> {

  <A, B> __<f, B> bind(__<f, A> fa, Function<A, __<f, B>> f);
  
}

Este metodo lo unico que hace es dado un tipo A en un contexto f y una funcion que transforma de A a B en el contexto f devolver B en el contexto f

Ademas, gracias a este nuevo metodo podemos derivar el metodo apply e implementarlo en nuestra interfaz quedando la clase asi

public interface Monad<f> extends Applicative<f> {

  <A, B> __<f, B> bind(__<f, A> fa, Function<A, __<f, B>> f);

  @Override
  default <A, B> __<f, B> apply(__<f, A> fa, __<f, Function<A, B>> f) {
    return bind(f, (f0 -> map(fa, f0)));
  }
}

Quizas suene algo enrevesado pero con un ejemplo se puede ver mucho mejor

Para este ejemplo vamos a usar la clase Optional de java. Primero debemos crear una clase para poder usar Optional como contexto

@HktConfig(withVisibility = Visibility.Same)
public class OptionalT<t> implements __<OptionalT, t> {

  public final Optional<t> optional;

  public OptionalT(Optional<t> optional) {
    this.optional = optional;
  }
}

Usamos la libreria hkt como en los ejemplos anteriores. De esta manera podemos embeber cualquier Optional dentro de un contexto

Ahora creamos nuestra monada de OptinalT implementando los 2 metodos necesarios: bind y point

public class MonadOptionalT implements Monad<OptionalT> {

  @Override
  public <A, B> __<OptionalT, B> bind(__<OptionalT, A> fa, Function<A, __<OptionalT, B>> f) {
    Optional<A> optional = Hkt.asOptionalT(fa).optional;
    return f.apply(optional.orElse(null));
  }

  @Override
  public <A> __<OptionalT, A> point(A a) {
    return new OptionalT<>(Optional.ofNullable(a));
  }
}

Como habiamos visto, Monad extiende de Applicative y en el post anterior ya implementamos Applicative asi que podemos dejar nuestro codigo mas simple usando la clase OptionalApplicative

public class MonadOptionalT extends OptionalApplicative implements Monad<OptionalT> {

  @Override
  public <A, B> __<OptionalT, B> bind(__<OptionalT, A> fa, Function<A, __<OptionalT, B>> f) {
    Optional<A> optional = Hkt.asOptionalT(fa).optional;
    return f.apply(optional.orElse(null));
  }
}

Ahora podemos usar las clases para ver algunos ejemplos concretos

MonadOptionalT monad = new MonadOptionalT();
Function<Integer, __<OptionalT, Integer>> fb = i -> monad.point(i == null ? null : i * 2);

__<OptionalT, Integer> point = monad.point(1);
__<OptionalT, Integer> bind = monad.bind(point, fb);
System.out.println(Hkt.asOptionalT(bind).optional);

point = monad.point(null);
bind = monad.bind(point, fb);
System.out.println(Hkt.asOptionalT(bind).optional);

La salida de este codigo es

Optional[2]
Optional.empty

Hasta aqui el post dedicado a las monadas. En el siguiente post ya veremos como podemos usar estas nuevas estructuras para crear programas reutilizando la logica de negocio sin tener en cuenta la implementacion