从右到左的应用扫描参数

Applicative scanning arguments right to left

我遇到过几种情况,我想使用应用样式 f <$> x1 <*> x2 <*> x3 但从右到左而不是通常的从左到右扫描应用参数。

自然地,如果我把它放到一个单子上下文中,我可以毫无问题地做到这一点:

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' }

所以,对于我的问题:是否有一些通用的方法可以在只有 Applicative(可能是一个新类型的包装器)的情况下完成这个,如果没有,为什么不能有一个。也就是说,欢迎任何有关此问题的优雅解决方案或变通方法的见解。

旁白:我的解决方案是定义新的右结合运算符,但这个解决方案一点也不优雅。

编辑:这是我的解决方案(我很想知道标准库中是否有等效的东西),如果我需要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' }

Naturally, if I bring this into a monadic context, I can do this without problem:

不要忘记 f 只是一个函数。因此,您可以简单地定义另一个函数,该函数以另一种顺序接受参数,然后回退到通常的应用组合器:

-- | 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

不过,仅用运算符来编写它可能是不可能的,或者很难。这是由于部分应用的性质。请记住,对于 f :: Int -> Char -> BoolApplicative f => f Int,表达式 f <$> x 类型为 Applicative f => f (Char -> Bool)。我们总是 "lose" 在左端打字,而不是在右端打字。如果你改变参数的顺序,又很容易了:

(>*>) :: 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

Backwards 类型与您的 Reverse 类似,并且在半标准包装中。