Can liftM diferem de Lifta?
-
06-07-2019 - |
Pergunta
De acordo com a o Typeclassopedia (entre outras fontes), Applicative
logicamente pertence entre Monad
e Pointed
(e, portanto, Functor
) na hierarquia de classes tipo, por isso, idealmente, ter algo como isto se o prelúdio Haskell foram escritos hoje:
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
(Onde essas definições padrão foram re-digitada por mim desde o na Wikipedia , erros de ser meu próprio, mas se houver erros, é, pelo menos em princípio, possível.)
Como as bibliotecas estão actualmente definidos, temos:
liftA :: (Applicative f) => (a -> b) -> f a -> f b
liftM :: (Monad m) => (a -> b) -> m a -> m b
e
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
ap :: (Monad m) => m (a -> b) -> m a -> m b
Observe a semelhança entre estes tipos dentro de cada par.
A minha pergunta é: são liftM
(distinto do liftA
) e ap
(distinto do <*>
), simplesmente um resultado da realidade histórica que Monad
não foi projetado com Pointed
e Applicative
em mente? Ou eles são de alguma outra forma comportamental (potencialmente, para algumas definições Monad
legais) distinta das versões que só exigem um contexto Applicative
?
Se eles são distintos, você poderia fornecer um simples conjunto de definições (obedecendo as leis exigidas de Monad
, Applicative
, Pointed
, e definições Functor
descritos no Typeclassopedia e em outros lugares, mas não imposta pelo sistema de tipo) para o qual liftA
e liftM
se comportar de forma diferente?
Como alternativa, se eles não são diferentes, você poderia provar a sua equivalência usando essas mesmas leis como premissas?
Solução
liftA
, liftM
, fmap
e .
deve ser todos a mesma função, e deve ser, desde que obedeçam a lei functor:
fmap id = id
No entanto, isso não é verificado por Haskell.
Agora, para Applicative. É possível que ap
e <*>
ser distintas para alguns functors simplesmente porque pode haver mais do que uma implementação que satisfaz os tipos e as leis. Por exemplo, a lista tem mais de uma instância Applicative
possível. Você poderia declarar um aplicativo da seguinte forma:
instance Applicative [] where
(f:fs) <*> (x:xs) = f x : fs <*> xs
_ <*> _ = []
pure = repeat
A função ap
ainda seria definido como liftM2 id
, que é a instância Applicative
que vem gratuitamente com cada Monad
. Mas aqui você tem um exemplo de um construtor de tipo ter mais de uma instância Applicative
, ambos satisfazer as leis. Mas se as mônadas e seus functors aplicativas discordar, é considerada boa forma de ter tipos diferentes para eles. Por exemplo, a instância Applicative
acima não concorda com a Mônada para []
, então você deve realmente dizer newtype ZipList a = ZipList [a]
e depois fazer a nova instância para ZipList
vez de []
.
Outras dicas
Eles pode diferentes, mas eles não deve .
Eles podem diferir porque eles podem ter diferentes implementações: uma é definida em uma instance Applicative
enquanto o outro é definido em um instance Monad
. Mas se eles realmente são diferentes, então eu diria que o programador que escreveu essas instâncias escreveu enganosa código.
Você tem razão: existem as funções como o fazem por razões históricas. As pessoas têm idéias fortes sobre como as coisas deveriam ter sido.