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:
-
Monoid (Kleisli maa)
conreturn
y composición de Kleisli. -
MonadPlus m => Monoid (Kleisli mab)
levantamzero
ymplus
lamplus
sobre->
-
Monoid b => Monoid (Kleisli mab)
levanta la estructura monoide deb
sobrem
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
[...]