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