Voy a utilizar una descripción independiente del lenguaje de mónadas como esta, describiendo primero los monoides:
Un monoide es (aproximadamente) un conjunto de funciones que toman algún tipo de parámetro y devuelven el mismo tipo.
Una mónada es (aproximadamente) un conjunto de funciones que toman un tipo de contenedor como parámetro y devuelve el mismo tipo de contenedor.
Tenga en cuenta que esas son descripciones, no definiciones. ¡Siéntase libre de atacar esa descripción!
Entonces, en un lenguaje OO, una mónada permite composiciones de operaciones como:
Flier<Duck> m = new Flier<Duck>(duck).takeOff().flyAround().land()
Tenga en cuenta que la mónada define y controla la semántica de esas operaciones, en lugar de la clase contenida.
Tradicionalmente, en un lenguaje OO, usaríamos una jerarquía de clases y herencia para proporcionar esa semántica. Así tendríamos una Bird
clase con métodos takeOff()
, flyAround()
y land()
, y el Pato heredaríamos aquellos.
Pero luego nos metemos en problemas con las aves no voladoras, porque penguin.takeOff()
falla. Tenemos que recurrir al lanzamiento y manejo de excepciones.
Además, una vez que decimos que Penguin es a Bird
, nos encontramos con problemas de herencia múltiple, por ejemplo, si también tenemos una jerarquía de Swimmer
.
Esencialmente estamos tratando de poner las clases en categorías (con disculpas a los muchachos de la teoría de la categoría) y definir la semántica por categoría en lugar de en clases individuales. Pero las mónadas parecen un mecanismo mucho más claro para hacerlo que las jerarquías.
Entonces, en este caso, tendríamos una Flier<T>
mónada como el ejemplo anterior:
Flier<Duck> m = new Flier<Duck>(duck).takeOff().flyAround().land()
... y nunca instanciaríamos a Flier<Penguin>
. Incluso podríamos usar la escritura estática para evitar que eso suceda, tal vez con una interfaz de marcador. O la verificación de la capacidad de tiempo de ejecución para rescatar. Pero en realidad, un programador nunca debe poner un pingüino en el volante, en el mismo sentido, nunca debe dividir por cero.
Además, es más generalmente aplicable. Un viajero no tiene que ser un pájaro. Por ejemplo Flier<Pterodactyl>
, o Flier<Squirrel>
, sin cambiar la semántica de esos tipos individuales.
Una vez que clasificamos la semántica por funciones componibles en un contenedor, en lugar de con jerarquías de tipos, resuelve los viejos problemas con las clases que "tipo de hacer, tipo de no" encajan en una jerarquía particular. También permite fácil y claramente múltiples semánticas para una clase, así Flier<Duck>
como también Swimmer<Duck>
. Parece que hemos estado luchando con un desajuste de impedancia al clasificar el comportamiento con las jerarquías de clase. Las mónadas lo manejan con elegancia.
Entonces, mi pregunta es, de la misma manera que hemos llegado a favorecer la composición sobre la herencia, ¿también tiene sentido favorecer a las mónadas sobre la herencia?
(Por cierto, no estaba seguro de si esto debería estar aquí o en Comp Sci, pero esto parece más un problema de modelado práctico. Pero tal vez sea mejor allí).