Con la introducción de Apply y Applicative, podemos alejarnos y ver toda una familia de clases de tipos que se ocupan de secuenciar cálculos de diferentes maneras.
Cada clase de tipo en la jerarquía representa un conjunto particular de semántica de secuenciación, presenta un conjunto de métodos característicos y define la funcionalidad de sus supertipos en términos de ellos:
- toda mónada es un aplicativo;
- cada aplicativo un semigrupal;
- y así.
Debido a la naturaleza de las relaciones entre las type class, las relaciones de herencia son constantes en todas las instancias.
Apply define product en términos de ap y map; Monad define product, ap y map, en términos de pure y flatMap.
Para ilustrar esto, consideremos dos tipos de datos hipotéticos:
• Foo es una mónada. Tiene una instancia de la type class Monad que implementa pure y flatMap y hereda definiciones estándar de product, map y ap;
• Bar es un funtor aplicativo. Tiene una instancia de Applicative que implementa pure y ap y hereda definiciones estándar de product y map.
¿Qué podemos decir sobre estos dos tipos de datos sin saber más sobre su implementación?
Sabemos estrictamente más sobre Foo que sobre Bar: Monad es un subtipo de Applicative, por lo que podemos garantizar propiedades de Foo (a saber, flatMap) que no podemos garantizar con Bar. Por el contrario, sabemos que Bar puede tener una gama más amplia de comportamientos que Foo. Tiene menos leyes que obedecer (sin flatMap), por lo que puede implementar comportamientos que Foo no puede.
Esto demuestra el clásico intercambio de poder (en el sentido matemático) versus restricción. Cuantas más restricciones imponemos a un tipo de datos, más garantías tenemos sobre su comportamiento, pero menos comportamientos podemos modelar.
Las mónadas resultan ser un punto dulce en esta compensación. Son lo suficientemente flexibles para modelar una amplia gama de comportamientos y lo suficientemente restrictivos para dar garantías sólidas sobre esos comportamientos. Sin embargo, hay situaciones en las que las mónadas no son la herramienta adecuada para el trabajo. A veces queremos comida tailandesa y los burritos simplemente no satisfacen.
Mientras que las mónadas imponen una secuencia estricta en los cálculos que modelan, los aplicativos y los semigrupos no imponen tal restricción. Esto los coloca en un punto dulce diferente en la jerarquía. Podemos usarlos para representar clases de cálculos paralelos/independientes que las mónadas no pueden.
Elegimos nuestra semántica eligiendo nuestras estructuras de datos. Si elegimos una mónada, obtenemos una secuencia estricta. Si elegimos un aplicativo, perdemos la capacidad de flatMap. Esta es la compensación impuesta por las leyes de consistencia. ¡Así que elige tus tipos con cuidado!
Si bien las mónadas y los funtores son los tipos de datos de secuenciación más utilizados, los semigrupos y los aplicativos son los más generales.
Estas clases de tipos proporcionan un mecanismo genérico para combinar valores y aplicar funciones dentro de un contexto, a partir del cual podemos crear mónadas y una variedad de otros combinadores.
Semigroupal y Applicative se usan más comúnmente como un medio para combinar valores independientes, como los resultados de las reglas de validación. Cats proporciona el tipo Validated para este propósito específico, junto con la sintaxis de aplicación como una forma conveniente de expresar la combinación de reglas.