如何组合两个组合的应用函子?
How to combine two composed applicative functors?
我有两个组合的应用函子 Maybe [Integer]
并想将它们与 <$>
/<*>
组合,但我坚持应用应用操作。以下不进行类型检查:
(<*>) (<*>) ((<$>) ((+) <$>) $ Just [1,2,3]) $ Just [4,5,6]
预期结果:
Just [5,6,7,6,7,8,7,8,9]
仿函数部分起作用,即作为第一个参数传递给 <*>
的中间值是 Just [Integer -> Integer]
。我习惯了 S 表达式,所以我很难使用 Haskell 语法。我知道 Compose
但我只对没有抽象的组合感兴趣。
liftA2
可能比 (<*>)
.
更容易混淆
(+) :: Int -> Int -> Int
liftA2 (+) :: [Int] -> [Int] -> [Int]
liftA2 (liftA2 (+)) :: Maybe [Int] -> Maybe [Int] -> Maybe [Int]
liftA2 (liftA2 (+)) (Just [1,2,3]) (Just [4,5,6])
作为 Li-yao Xia ,使用 liftA2
可以减少很多混淆。
但是如果你还是看它在底层操作方面变成了什么,我们可以展开定义liftA2
:
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f x y = f <$> x <*> y
所以解变成
(liftA2 . liftA2) (+) (Just [1,2,3]) (Just [4,5,6])
= liftA2 (liftA2 (+)) (Just [1,2,3]) (Just [4,5,6])
= (\f x y -> f <$> x <*> y) ((\f x y -> f <$> x <*> y) (+)) (Just [1,2,3]) (Just [4,5,6])
= ((\f x y -> f <$> x <*> y) (+)) <$> Just [1,2,3] <*> Just [4,5,6]
= (\x y -> (+) <$> x <*> y) <$> Just [1,2,3] <*> Just [4,5,6]
现在,这不像上面的示例那样采用无点风格,我真的认为将其转换为无点风格没有帮助,但这是 http://pointfree.io 的输出:
((<*>) . ((+) <$>)) <$> Just [1, 2, 3] <*> Just [4, 5, 6]
我们通过eta-expanding可以看出这是一样的:
(<*>) . ((+) <$>)
= \x y -> ((<*>) . ((+) <$>)) x y
= \x y -> ((<*>) $ ((+) <$>) x) y
= \x y -> ((<*>) ((+) <$> x)) y
= \x y -> (<*>) ((+) <$> x) y
= \x y -> ((+) <$> x) <*> y
= \x y -> (+) <$> x <*> y
另一种方法是使用 ListT
transformer。虽然在这种情况下它工作得很好,但由于某种原因它是一个折旧的变压器,用红色标记 "Deprecated: This transformer is invalid on most monads".
import Control.Monad.Trans.List
doit :: (Int-> Int -> Int) -> Maybe [Int] -> Maybe [Int] -> Maybe [Int]
doit f mt1 mt2 = runListT $ f <$> (ListT mt1) <*> (ListT mt2)
λ> doit (+) (Just [1,2,3]) (Just [4,5,6])
Just [5,6,7,6,7,8,7,8,9]
两个 Applicative
的组合始终是一个 Applicative
(与 Monad
的情况不同)。
我们可以利用 Data.Functor.Compose
中的 Compose
newtype
来发挥我们的优势:
newtype Compose f g a = Compose { getCompose :: f (g a) }
它需要一些包装,但这种解决方案在适当的情况下可能会有用:
example :: Maybe [Int]
example =
getCompose ((+) <$> Compose (Just [1,2,3]) <*> Compose (Just [4,5,6]))
我有两个组合的应用函子 Maybe [Integer]
并想将它们与 <$>
/<*>
组合,但我坚持应用应用操作。以下不进行类型检查:
(<*>) (<*>) ((<$>) ((+) <$>) $ Just [1,2,3]) $ Just [4,5,6]
预期结果:
Just [5,6,7,6,7,8,7,8,9]
仿函数部分起作用,即作为第一个参数传递给 <*>
的中间值是 Just [Integer -> Integer]
。我习惯了 S 表达式,所以我很难使用 Haskell 语法。我知道 Compose
但我只对没有抽象的组合感兴趣。
liftA2
可能比 (<*>)
.
(+) :: Int -> Int -> Int
liftA2 (+) :: [Int] -> [Int] -> [Int]
liftA2 (liftA2 (+)) :: Maybe [Int] -> Maybe [Int] -> Maybe [Int]
liftA2 (liftA2 (+)) (Just [1,2,3]) (Just [4,5,6])
作为 Li-yao Xia liftA2
可以减少很多混淆。
但是如果你还是看它在底层操作方面变成了什么,我们可以展开定义liftA2
:
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f x y = f <$> x <*> y
所以解变成
(liftA2 . liftA2) (+) (Just [1,2,3]) (Just [4,5,6])
= liftA2 (liftA2 (+)) (Just [1,2,3]) (Just [4,5,6])
= (\f x y -> f <$> x <*> y) ((\f x y -> f <$> x <*> y) (+)) (Just [1,2,3]) (Just [4,5,6])
= ((\f x y -> f <$> x <*> y) (+)) <$> Just [1,2,3] <*> Just [4,5,6]
= (\x y -> (+) <$> x <*> y) <$> Just [1,2,3] <*> Just [4,5,6]
现在,这不像上面的示例那样采用无点风格,我真的认为将其转换为无点风格没有帮助,但这是 http://pointfree.io 的输出:
((<*>) . ((+) <$>)) <$> Just [1, 2, 3] <*> Just [4, 5, 6]
我们通过eta-expanding可以看出这是一样的:
(<*>) . ((+) <$>)
= \x y -> ((<*>) . ((+) <$>)) x y
= \x y -> ((<*>) $ ((+) <$>) x) y
= \x y -> ((<*>) ((+) <$> x)) y
= \x y -> (<*>) ((+) <$> x) y
= \x y -> ((+) <$> x) <*> y
= \x y -> (+) <$> x <*> y
另一种方法是使用 ListT
transformer。虽然在这种情况下它工作得很好,但由于某种原因它是一个折旧的变压器,用红色标记 "Deprecated: This transformer is invalid on most monads".
import Control.Monad.Trans.List
doit :: (Int-> Int -> Int) -> Maybe [Int] -> Maybe [Int] -> Maybe [Int]
doit f mt1 mt2 = runListT $ f <$> (ListT mt1) <*> (ListT mt2)
λ> doit (+) (Just [1,2,3]) (Just [4,5,6])
Just [5,6,7,6,7,8,7,8,9]
两个 Applicative
的组合始终是一个 Applicative
(与 Monad
的情况不同)。
我们可以利用 Data.Functor.Compose
中的 Compose
newtype
来发挥我们的优势:
newtype Compose f g a = Compose { getCompose :: f (g a) }
它需要一些包装,但这种解决方案在适当的情况下可能会有用:
example :: Maybe [Int]
example =
getCompose ((+) <$> Compose (Just [1,2,3]) <*> Compose (Just [4,5,6]))