Despues de la primera parte de la serie de articulos de programacion funcional vamos con la segunda donde veremos el Funtor Aplicativo

El concepto y su utilizacion viene motivada por la siguiente necesidad: queremos aplicar una funcion que esta dentro de un funtor a un valor dentro de un funtor. Esto se entiende mejor con un ejemplo:

Tenemos un Optional de Integer

Optional<Integer> opi = Optional.of(1);

Queremos aplicarle una funcion que tambien esta dentro de Optional

Optional<Function<Integer, String>> opf = Optional.of(String::valueOf);

Nuestra funcion apply quedaria asi

public Optional<String> apply(Optional<Integer> fa, Optional<Function<Integer, String>> f) {
  String s = fa.map(f.orElse(String::valueOf)).orElse(null);
  return Optional.of(s);
}

Ya solo tenemos que usar nuestra funcion

Optional<String> aps= apply(opi, opf);

Funtor aplicativo

Vamos a generificar esta clase usando la libreria hkt. Ademas como os habia comentado, un aplicativo es un funtor asi que extenderemos nuestra interfaz de Functor

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

  <A> __<f, A> point(A a);

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

}

Hemos añadido una funcion mas, point. Esta funcion toma un valor de tipo A y devuelve un aplicativo con ese valor dentro. Gracias a este metodo podemos definir la funcion map del funtor derivandola de las otras 2

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

  <A> __<f, A> point(A a);

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

  default <A, B> __<f, B> map(__<f, A> fa, Function<A, B> f) {
    return apply(fa, point(f));
  }
}

Implementemos el aplicativo para Optional pero antes tenemos que hacer el wrapper para la clase Optional

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

  public final Optional<t> optional;

  public OptionalT(Optional<t> optional) {
    this.optional = optional;
  }
}
public class OptionalApplicative implements Applicative<OptionalT> {

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

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

Probemos nuestro aplicativo

OptionalApplicative applicative = new OptionalApplicative();

OptionalT<Integer> opi = new OptionalT<>(Optional.of(1));
OptionalT<Function<Integer, String>> opf = new OptionalT<>(Optional.of(String::valueOf));
__<OptionalT, String> apply = applicative.apply(opi, opf);
System.out.println(Hkt.asOptionalT(apply).optional);

opi = new OptionalT<>(Optional.ofNullable(null));
apply = applicative.apply(opi, opf);
System.out.println(Hkt.asOptionalT(apply).optional);

opi = new OptionalT<>(Optional.of(1));
opf = new OptionalT<>(Optional.ofNullable(null));
System.out.println(Hkt.asOptionalT(apply).optional);

Con este codigo el resultado obtenido es el siguiente

Optional[1]
Optional.empty
Optional.empty

Gracias a que Applicative es un Functor podemos usar la funcion map «gratis»

OptionalApplicative applicative = new OptionalApplicative();

OptionalT<Integer> opi = new OptionalT<>(Optional.of(1));
Function<Integer, String> opf = String::valueOf;
__<OptionalT, String> apply = applicative.map(opi, opf);
System.out.println(Hkt.asOptionalT(apply).optional);

Obteniendo el siguiente resultado

Optional[1]