haskell monads category-theory monoids

haskell - Ejemplos simples para ilustrar Category, Monoid y Monad?



monads category-theory (1)

Me estoy confundiendo mucho con estos tres conceptos.

¿Hay algún ejemplo simple para ilustrar las diferencias entre Category, Monoid y Monad?

Sería muy útil si hay una ilustración de estos conceptos abstractos.


Probablemente esta no sea la respuesta que está buscando, pero de todos modos, aquí va:

Una forma realmente torcida de mirar las mónadas y la cooperación.

Una forma de ver conceptos abstractos como estos es vincularlos con conceptos básicos, como operaciones ordinarias de procesamiento de listas. Entonces, podrías decir eso,

  • Una categoría generaliza la operación (.) .
  • Un monoide generaliza la operación (++) .
  • Un functor generaliza la operación del map .
  • Un functor aplicativo generaliza la operación zip (o zipWith ).
  • Una mónada generaliza la operación de concat .

Una categoría

Una categoría consiste en un conjunto (o una clase) de objetos y un grupo de flechas que conectan dos de los objetos. Además, para cada objeto, debería haber una flecha de identidad que conecta este objeto consigo mismo. Además, si hay una flecha ( f ) que termina en un objeto y otra ( g ) que parte del mismo objeto, entonces también debe haber una flecha compuesta llamada g . f g . f .

En Haskell esto se modela como una clase de tipo que representa la categoría de los tipos de Haskell como objetos.

class Category cat where id :: cat a a (.) :: cat b c -> cat a b -> cat a c

Los ejemplos básicos de una categoría son funciones. Cada función conecta dos tipos, para todos los tipos, existe la función id :: a -> a que conecta el tipo (y el valor) consigo mismo. La composición de las funciones es la composición de la función ordinaria.

En resumen, las categorías en la base de Haskell son cosas que se comportan como funciones , es decir, puedes poner una tras otra con una versión generalizada de (.) .

Un monoide

Un monoide es un conjunto con un elemento de unidad y una operación asociativa. Esto está modelado en Haskell como:

class Monoid a where mempty :: a mappend :: a -> a -> a

Ejemplos comunes de monoides incluyen:

  • conjunto de enteros, el elemento 0 y la operación (+) .
  • conjunto de enteros positivos, el elemento 1 y la operación (*) .
  • conjunto de todas las listas, la lista vacía [] y la operación (++) .

Estos están modelados en Haskell como

newtype Sum a = Sum {getSum :: a} instance (Num a) => Monoid (Sum a) where mempty = Sum 0 mappend (Sum a) (Sum b) = Sum (a + b) instance Monoid [a] where mempty = [] mappend = (++)

Los monoides se utilizan para "combinar" y acumular cosas. Por ejemplo, la función mconcat :: Monoid a => [a] -> a , se puede usar para reducir una lista de sumas a una sola suma, o una lista anidada en una lista plana. Considere esto como una especie de generalización de operaciones (++) o (+) que de alguna manera "fusionan" dos cosas.

A Functor

Un functor en Haskell es una cosa que generaliza de manera bastante directa el map :: (a->b) -> [a] -> [b] operaciones map :: (a->b) -> [a] -> [b] . En lugar de mapear sobre una lista, se correlaciona con alguna estructura , como una lista, un árbol binario o incluso una operación IO. Los funtores se modelan así:

class Functor f where fmap :: (a->b) -> f a -> f b

Contraste esto con la definición de la función de map normal.

Un Funcionador Aplicativo

Los funtores aplicativos se pueden ver como cosas con una operación zipWith general con zipWith . Los funtores hacen un mapa de las estructuras generales una a la vez, pero con un funcionador Aplicativo puede unir dos o más estructuras. Para el ejemplo más simple, puede usar los aplicativos para comprimir dos enteros dentro del tipo Maybe :

pure (+) <*> Just 1 <*> Just 2 -- gives Just 3

Tenga en cuenta que la estructura puede afectar el resultado, por ejemplo:

pure (+) <*> Nothing <*> Just 2 -- gives Nothing

Contraste esto a la función zipWith habitual:

zipWith (+) [1] [2]

En lugar de solo listas, el aplicativo funciona para todo tipo de estructuras. Además, el truco ingenioso con pure y (<*>) generaliza la compresión para trabajar con cualquier cantidad de argumentos. Para ver cómo funciona esto, inspeccione los siguientes tipos mientras mantiene el concepto de funciones parcialmente aplicadas a mano:

instance (Functor f) => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b

Observe también la similitud entre fmap y (<*>) .

Una mónada

Las mónadas a menudo se utilizan para modelar contextos computacionales diferentes, como cálculos no deterministas o de efecto lateral. Como ya hay demasiados tutoriales de mónada, solo recomendaré el mejor , en lugar de escribir otro más.

En relación con las funciones de procesamiento de listas ordinarias, las mónadas generalizan la función concat :: [[a]] -> [a] para trabajar con muchos otros tipos de estructuras además de listas. Como un ejemplo simple, la join operación monádica se puede usar para aplanar los valores Maybe anidados:

join (Just (Just 42)) -- gives Just 42 join (Just (Nothing)) -- gives Nothing

¿Cómo se relaciona esto con el uso de las Mónadas como medio para estructurar los cálculos? Considere un ejemplo de juguete donde hace dos consultas consecutivas desde alguna base de datos. La primera consulta le devuelve un valor clave, con el que desea hacer otra búsqueda. El problema aquí es que el primer valor está dentro de Maybe , por lo que no puede consultarlo directamente. En cambio, como quizás sea un Functor , en su lugar podría fmap el valor de retorno con la nueva consulta. Esto le daría dos valores anidados de Maybe como el anterior. Otra consulta daría como resultado tres capas de Maybe s. Esto sería bastante difícil de programar, pero una join monádica te ofrece una forma de aplanar esta estructura y trabajar con un solo nivel de Maybe s.

(Creo que voy a editar mucho esta publicación antes de que tenga sentido ...)