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?

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top