Selon le célèbre article Les idiomes sont inconscients, les flèches sont méticuleuses, les monades sont libertines. le pouvoir expressif des flèches (sans classes de types supplémentaires) devrait se situer strictement entre les foncteurs applicatifs et les monades : les monades sont équivalentes à ArrowApply
et Applicative
devraient être équivalentes à ce que l'article appelle des "flèches statiques". Cependant, je ne vois pas très bien ce que signifie la restriction de ce caractère "statique".
En jouant avec les trois classes de types en question, j'ai pu construire une équivalence entre les foncteurs applicatifs et les flèches, que je présente ci-dessous dans le contexte de l'équivalence bien connue entre les flèches et les foncteurs applicatifs. Monad
y ArrowApply
. Cette construction est-elle correcte ? (J'ai prouvé la plupart des lois des flèches avant de s'en lasser). Cela ne signifie-t-il pas que Arrow
y Applicative
sont exactement les mêmes ?
{-# LANGUAGE TupleSections, NoImplicitPrelude #-}
import Prelude (($), const, uncurry)
-- In the red corner, we have arrows, from the land of * -> * -> *
import Control.Category
import Control.Arrow hiding (Kleisli)
-- In the blue corner, we have applicative functors and monads,
-- the pride of * -> *
import Control.Applicative
import Control.Monad
-- Recall the well-known result that every monad yields an ArrowApply:
newtype Kleisli m a b = Kleisli{ runKleisli :: a -> m b}
instance (Monad m) => Category (Kleisli m) where
id = Kleisli return
Kleisli g . Kleisli f = Kleisli $ g <=< f
instance (Monad m) => Arrow (Kleisli m) where
arr = Kleisli . (return .)
first (Kleisli f) = Kleisli $ \(x, y) -> liftM (,y) (f x)
instance (Monad m) => ArrowApply (Kleisli m) where
app = Kleisli $ \(Kleisli f, x) -> f x
-- Every arrow arr can be turned into an applicative functor
-- for any choice of origin o
newtype Arrplicative arr o a = Arrplicative{ runArrplicative :: arr o a }
instance (Arrow arr) => Functor (Arrplicative arr o) where
fmap f = Arrplicative . (arr f .) . runArrplicative
instance (Arrow arr) => Applicative (Arrplicative arr o) where
pure = Arrplicative . arr . const
Arrplicative af <*> Arrplicative ax = Arrplicative $
arr (uncurry ($)) . (af &&& ax)
-- Arrplicatives over ArrowApply are monads, even
instance (ArrowApply arr) => Monad (Arrplicative arr o) where
return = pure
Arrplicative ax >>= f =
Arrplicative $ (ax >>> arr (runArrplicative . f)) &&& id >>> app
-- Every applicative functor f can be turned into an arrow??
newtype Applicarrow f a b = Applicarrow{ runApplicarrow :: f (a -> b) }
instance (Applicative f) => Category (Applicarrow f) where
id = Applicarrow $ pure id
Applicarrow g . Applicarrow f = Applicarrow $ (.) <$> g <*> f
instance (Applicative f) => Arrow (Applicarrow f) where
arr = Applicarrow . pure
first (Applicarrow f) = Applicarrow $ first <$> f
0 votes
Quel problème rencontrez-vous avec les flèches statiques ? L'isomorphisme est affiché sur la première page de l'article. Il dit que les applicatifs sont équivalents aux flèches
arr
équipé d'un isomorphisme entrearr a b
yarr () (a -> b)
.0 votes
@TomEllis : Laissez-moi essayer de formuler pourquoi je ne comprends pas cela. Supposons que nous ayons une classe de type
class (Arrow arr) => ArrowStatic arr where iso :: arr a b :<->: arr () (a -> b)
. Maintenant, siApplicative
est équivalent àArrowStatic
cela ne veut-il pas dire que je peux transformer n'importe quelleApplicative
enArrowStatic
et donc,Arrow
? En quoi cela implique-t-il queArrow
est une interface plus expressive ?3 votes
Il existe une preuve partielle que
Category
+Applicative
+ quelques lois supplémentaires pour l'interaction entre les deux, c'est la même chose queArrow
. cdsmith.wordpress.com/2011/08/13/ . La preuve ne va pas jusqu'à prouver que n'importe quelApplicative
Category
est également unArrow
. Nous avons eu une discussion similaire surArrows
yApplicative
Inspiré par cette réponse tangentiellement liée : stackoverflow.com/a/22875729/4144130 votes
Pourquoi le wrapper newtype
Arrplicative
nécessaire ? Pourquoi ne pas simplementinstance Arrow arr => Functor (arr o) where fmap f = (arr f .)
? L'autre, je peux la comprendre.2 votes
@ErikAllik, ces instances sont un mal qui se chevauche. Avec cette instance autour et sans
OverlappingInstances
le sólo pour quelque chose qui ressemble àp x
pour être unFunctor
est pourp
pour être unArrow
! Ça casse toutes sortes de choses, alors Cactus a réfléchi et ne l'a pas fait.0 votes
Ohhh... J'y ai pensé mais je n'avais pas réalisé.
arr o
était extrêmement générique. Mais là encore, la contrainteArrow are => arr o
n'aidera pas du tout ? Pas même à la dernière étape de la recherche ? Pas même avec une extension GHC ?1 votes
@EricAllik, non, pas du tout, pour autant que je sache. GHC commet l'instance avant de résoudre ses contraintes. En raison de
OverlappingInstances
(blech !), cela finit par être une bonne chose pour les contraintes d'égalité. Sinon, je pense que cela peut être important pour une résolution efficace - je ne suis pas sûr. Et je pense que c'est presque certainement important pour rendre les choses saines. sans chevauchement/incohérence.0 votes
En Scala, il n'y a pas de telles limitations, et cela fonctionne très bien - comme pour Prolog. Parfois, on obtient deux possibilités ou plus, mais ce n'est un problème qu'au stade final, et même dans ce cas, je pense qu'il est possible de réduire le nombre de possibilités à une seule. J'aimerais donc comprendre quelle est la raison théorique de type, de catégorie ou autre pour laquelle Haskell a décidé de ne pas tenir compte de l'aspect Prolog dans son résolveur d'instance implicite... oups, je veux dire.
0 votes
@ErikAllik, je doute que vous trouviez une réponse dans la théorie des catégories, ou même nécessairement dans la théorie des types. Vous devriez poser la question quelque part. Peut-être haskell-cafe ou ghc-devs ?