从右到左的应用扫描参数
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 -> Bool
和 Applicative 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
类似,并且在半标准包装中。
我遇到过几种情况,我想使用应用样式 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 -> Bool
和 Applicative 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
类似,并且在半标准包装中。