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]