Question

Selon Typeclassopedia (entre autres sources), Applicative appartient logiquement entre Monad et Pointed (et donc Functor) dans la hiérarchie des classes de types; nous aurions donc idéalement quelque chose comme ceci si le prélude Haskell était écrit aujourd'hui:

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

(Où ces définitions par défaut ont été retapées par moi à partir de la explication sur Wikipedia , les erreurs étant les miennes, mais s’il existe des erreurs, c’est au moins en principe possible.)

Comme les bibliothèques sont actuellement définies, nous avons:

liftA :: (Applicative f) => (a -> b) -> f a -> f b
liftM ::       (Monad m) => (a -> b) -> m a -> m b

et:

(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
ap    ::       (Monad m) => m (a -> b) -> m a -> m b

Notez la similitude entre ces types dans chaque paire.

Ma question est la suivante: sont liftM (par opposition à liftA) et ap (par opposition à <*>), tout simplement le résultat de la réalité historique selon laquelle <=> n'a pas été conçu avec <= > et <=> à l'esprit? Ou sont-ils d'une autre manière comportementale (potentiellement, pour certaines <=> définitions légales) distinctes des versions qui nécessitent uniquement un <=> contexte?

Si elles sont distinctes, pouvez-vous fournir un ensemble simple de définitions (respectant les lois requises des définitions <=>, <=>, <=> et <=> décrites dans Typeclassopedia et ailleurs, mais non appliquées par le type de système) pour lequel <=> et <=> se comportent différemment?

Sinon, si elles ne sont pas distinctes, pourriez-vous prouver leur équivalence en utilisant les mêmes lois que les locaux?

Était-ce utile?

La solution

liftA, liftM, fmap et . devraient avoir la même fonction et ils doivent être s'ils satisfont à la loi du foncteur :

fmap id = id

Cependant, cela n'est pas vérifié par Haskell.

Maintenant pour l'application. Il est possible que ap et <*> soient distincts pour certains foncteurs simplement parce qu'il peut y avoir plusieurs implémentations qui répondent aux types et aux lois. Par exemple, List a plusieurs instances possibles Applicative. Vous pouvez déclarer un applicatif comme suit:

instance Applicative [] where
  (f:fs) <*> (x:xs) = f x : fs <*> xs
  _      <*> _      = []
  pure              = repeat

La fonction liftM2 id serait toujours définie comme Monad, qui est l'instance [] fournie gratuitement avec chaque newtype ZipList a = ZipList [a]. Mais ici, vous avez un exemple de constructeur de type ayant plus d'une instance ZipList, les deux satisfaisant les lois. Mais si vos monades et vos foncteurs applicatifs ne sont pas d’accord, on considère qu’il est judicieux d’en avoir différents types. Par exemple, l'instance <=> ci-dessus ne correspond pas à la monade de <=>. Vous devez donc réellement dire <=>, puis créer la nouvelle instance pour <=> au lieu de <=>.

Autres conseils

Ils peuvent différer, mais ils ne devraient pas .

Ils peuvent différer car ils peuvent avoir différentes implémentations: l’une est définie dans un instance Applicative, tandis que l’autre est définie dans un instance Monad. Mais s’ils diffèrent, je dirais que le programmeur qui a écrit ces instances a écrit un code trompeur.

Vous avez raison: les fonctions existent comme elles le sont pour des raisons historiques. Les gens ont des idées bien arrêtées sur la façon dont les choses auraient dû se passer.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top