2016-04-16 5 views
1

J'ai rencontré plusieurs situations où je voudrais utiliser le style applicatif f <$> x1 <*> x2 <*> x3 mais en balayant les arguments applicatifs de droite à gauche au lieu des habituels de gauche à droite.Arguments de balayage applicables de droite à gauche

Naturellement, si je soulève cette question dans un contexte monadique, je peux le faire sans problème:

liftM3' :: Monad m => (a1 -> a2 -> a3 -> r) -> m a1 -> m a2 -> m a3 -> m r 
liftM3' f x1 x2 x3 = do { x3' <- x3; x2' <- x2; x1' <- x1; return f x1' x2' x3' } 

Alors, ma question: est-il une méthode générale pour accomplir cela dans le contexte de seulement Applicative (peut-être un wrapper newtype) et si non, pourquoi ne peut-il pas y en avoir un. Cela dit, tout aperçu des solutions élégantes ou des solutions de contournement à ce problème sont les bienvenus.

A côté: Ma solution avait été de définir de nouveaux opérateurs associatifs droits, mais la solution n'était en aucun cas élégante.

Modifier: Voici ma solution (je serais intéressé à savoir s'il y a quelque chose d'équivalent dans les bibliothèques standard), si je requiers Monad:

newtype Reverse m a = Reverse (m a) 

instance Monad m => Functor (Reverse m) where 
    f `fmap` x = pure f <*> x 

instance Monad m => Applicative (Reverse m) where 
    pure x = Reverse $ return x 
    (Reverse f) <*> (Reverse x) = Reverse $ do { x' <- x; f' <- f; return $ f' x' } 
+1

La proposition ['ApplicativeDo'] (https://ghc.haskell.org/trac/ghc/wiki/ApplicativeDo) pourrait vous intéresser. – chepner

Répondre

3

Le type Backwards est comme votre Reverse, et dans un emballage semi-standard.

+0

'transformers' est assez standard pour moi. Ceci est exactement ce que je cherchais. Merci! – Alec

+0

En fait, un autre suivi rapide, est-il un bon moyen simple d'inverser 'Applicative',' Foldable' et 'Traversable' tout à la fois? Peut-être avec 'Compose'? Je ne pouvais pas obtenir les types de travailler ... – Alec

+0

@Alec 'Reverse (Backwards f)', je suppose. Bien qu'il semble que vous pourriez devoir ajouter 'l'instance dérivante pliable f => Pliable (Backwards f)' ou similaire (et pour 'Traversable'). Je serais prêt à accepter en amont un correctif pour ajouter ces instances. –

0

Naturellement, si j'apporte ceci dans un contexte monadique, je peux le faire sans problème:

Ne pas oublier que f est juste une fonction. En tant que tel, vous pouvez simplement définir une autre fonction qui prend les arguments dans un autre ordre, puis retomber à combinateurs habituelles applicatives:

-- | Lifts the given function into an applicative context. 
-- The applicative effects are handled from right-to-left 
-- e.g. 
-- >>> liftA3 (\_ _ _ ->()) (putStr "a") (putStr "b") (putStr "c") 
-- will put "cba" on your console. 
liftA3Rev :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d 
liftA3Rev f x y z = f' <$> z <*> y <*> x 
    where 
    f' = \c b a -> f a b c 

Il est probablement soit impossible ou très difficile d'écrire ce avec des opérateurs seulement, cependant. Cela est dû à la nature de l'application partielle. Rappelez-vous que pour f :: Int -> Char -> Bool et Applicative f => f Int, l'expression f <$> x a le type Applicative f => f (Char -> Bool). Nous perdons toujours les types à l'extrémité gauche et non à l'extrémité droite. Si vous modifiez l'ordre des arguments, il est facile à nouveau:

(>*>) :: Applicative f => f a -> f (a -> b) -> f b 
(>*>) = flip (<*>) 
infixr 4 >*> -- right associative 

liftA3Rev' :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d 
liftA3Rev' f x y z = z >*> y >*> x >*> pure f