haskell monads monoids kleisli

haskell - ¿Por qué no es Kleisli una instancia de Monoid?



monads monoids (1)

En el negocio del diseño de bibliotecas, nos enfrentamos a un punto de elección aquí, y hemos elegido ser menos consistentes en nuestra política colectiva (o falta de ella).

Monoid instancias de Monad para constructores de tipo Monad (o Applicative ) pueden surgir de varias maneras. El levantamiento puntual siempre está disponible, pero no definimos

instance (Applicative f, Monoid x) => Monoid (f x) {- not really -} where mempty = pure mempty mappend fa fb = mappend <$> fa <*> fb

Tenga en cuenta que el instance Monoid (a -> b) es precisamente un levantamiento puntual, de modo que el levantamiento puntual para (a -> mb) ocurre siempre que la instancia de monoide para mb sí levante para el monoide en b .

No hacemos levantamiento puntual en general, no solo porque impediría otras instancias de Monoid cuyos portadores sean tipos aplicados, sino también porque la estructura de la f menudo se considera más significativa que la de la x . Un caso clave en este punto es el monoide libre , mejor conocido como [x] , que es un Monoid por [] y (++) , en lugar de por elevación puntual. La estructura monoidal proviene de la envoltura de la lista, no de los elementos envueltos.

Mi regla de oro preferida es, de hecho, priorizar la estructura monoidal inherente en el constructor de tipo sobre cualquier elevación puntual, o estructura monoidal de instancias específicas de un tipo, como la composición monoide para a -> a . Estos pueden y reciben envolturas newtype .

MonadPlus m argumentos sobre si Monoid (mx) debe coincidir con MonadPlus m cuando ambos existan (y de manera similar con Alternative ). Mi sensación es que la única buena instancia de MonadPlus es una copia de una instancia de Monoid , pero otras difieren. Aún así, la biblioteca no es consistente en este asunto, especialmente no en el tema de (muchos lectores habrán visto venir este viejo ardor de errores) ...

... la instancia de monoide para Maybe , que ignora el hecho de que habitualmente usamos Maybe para modelar posibles fallas y en su lugar observa que la misma idea de tipo de datos de chucking en un elemento adicional se puede usar para dar a un semigrupo un elemento neutral si ya no tenia uno Las dos construcciones dan origen a tipos isomorfos, pero no son conceptualmente afines. ( Editar Para empeorar las cosas, la idea se implementa de manera incómoda, lo que le da a la restricción de Semigroup una Semigroup cuando solo se necesita un Semigroup . Me gustaría ver implementada la idea de Semigroup -extends-to-Monoid, pero no para Maybe )

Volviendo a Kleisli en particular, tenemos tres instancias candidatas obvias:

  1. Monoid (Kleisli maa) con return y composición de Kleisli.
  2. MonadPlus m => Monoid (Kleisli mab) levanta mzero y mplus la mplus sobre ->
  3. Monoid b => Monoid (Kleisli mab) levanta la estructura monoide de b sobre m luego ->

Espero que no se haya hecho una elección, solo porque no está claro qué elección tomar. Dudo en decirlo, pero mi voto sería por 2, priorizando la estructura proveniente de Kleisli ma sobre la estructura proveniente de b .

Si desea agregar dos funciones de tipo (a -> mb) para obtener solo una función del mismo tipo que anexa ambos resultados, puede usar Kleisli para hacerlo:

instance (Monad m, Monoid b) => Monoid (Kleisli m a b) where mempty = Kleisli (/_ -> return mempty) mappend k1 k2 = Kleisli g where g x = do r1 <- runKleisli k1 x r2 <- runKleisli k2 x return (r1 <> r2)

Sin embargo, actualmente no hay tal instancia definida en Control.Arrow . Como a menudo, en Haskell, sospecho que hay una buena razón, pero no puedo encontrar cuál.

Nota

Esta pregunta es bastante similar a esta . Sin embargo, con Monoid no veo una manera de definir una instancia como:

instance (Monad m, Monoid b) => Monoid (a -> m b) where [...]

Ya que ya existe una instancia:

instance Monoid b => Monoid (a -> b) where [...]