Según the Typeclassopedia (entre otras fuentes), Applicative
pertenece lógicamente entre Monad
y Pointed
(y por lo tanto Functor
) en la jerarquía de clases de tipo, por lo que lo ideal sería tener algo como esto si el preludio Haskell fueron escritas hoy:¿Puede liftM diferir de liftA?
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Pointed f where
pure :: a -> f a
class Pointed f => Applicative f where
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
-- either the traditional bind operation
(>>=) :: (m a) -> (a -> m b) -> m b
-- or the join operation, which together with fmap is enough
join :: m (m a) -> m a
-- or both with mutual default definitions
f >>= x = join ((fmap f) x)
join x = x >>= id
-- with return replaced by the inherited pure
-- ignoring fail for the purposes of discussion
(. Cuando esas definiciones predeterminadas fueron re-introducidas por mí desde el explanation at Wikipedia, los errores de ser mi propio, pero si hay errores es al menos en principio es posible)
a medida que las bibliotecas se definen actualmente, tenemos:
liftA :: (Applicative f) => (a -> b) -> f a -> f b
liftM :: (Monad m) => (a -> b) -> m a -> m b
y:
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
ap :: (Monad m) => m (a -> b) -> m a -> m b
notar la similitud entre estos tipos dentro de cada par.
Mi pregunta es: ¿liftM
(a diferencia de liftA
) y ap
(a diferencia de <*>
), simplemente el resultado de la realidad histórica que Monad
no fue diseñado con Pointed
y Applicative
en mente? ¿O están de alguna otra manera conductual (potencialmente, para algunas definiciones legales Monad
) distintas de las versiones que solo requieren un contexto Applicative
?
Si son distintos, ¿Podría dar un simple conjunto de definiciones (obedeciendo las leyes necesarias de Monad
, Applicative
, Pointed
y Functor
definiciones descritas en el Typeclassopedia y en otros lugares, pero no impuestas por el sistema de tipos) para los que liftA
y liftM
se comportan de manera diferente?
Alternativamente, si no son distintas, ¿podría probar su equivalencia utilizando esas mismas leyes como premisa?
OK, eso es cierto, hay más de una instancia Aplicativo y más de una instancia Mónada para algunos tipos. Pero, según las definiciones anteriores, la implementación pure/return debería compartirse entre la instancia Applicative y la instancia de Monad. ¿Esto significa que, para cumplir las leyes, que <*> y ap tendrían que ser lo mismo? Parece que tu ejemplo se basa en hacer una instancia Aplicativo para [] = pura con la repetición, pero sigue utilizando el preludio ejemplo Mónada para [] con el regreso = ([])? –
puedo estar perdiendo algo, pero no es cierto que una función del tipo 'forall a, b. (a -> b) -> F a -> F b' se garantiza que es 'fmap'. Por ejemplo, considere 'notmap f xs = zipWith ($) (repeat (const. F $ head xs)) xs'. –
@Apocalisp: teniendo en cuenta sólo el tipo de fmap, todavía puede tener miembros exóticos. La condición lateral fmap id = id es suficiente para forzar el problema, y creo que el cuantificador extra que pones en 'f' es infiel. ;) Doug, mientras que la definición de Functor es única dadas las leyes, las leyes Aplicativas son lo suficientemente flexibles como para permitir múltiples definiciones conformes. Sin embargo, por convención, la Mónada y la Aplicativa para un tipo dado deben ser compatibles. –