我有两个组成的应用函子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
,但我对仅带有抽象的组合感兴趣。
作为夏丽瑶said,使用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
[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])
另一种方法可能是使用ListT
transformer。尽管在这种情况下它可以正常工作,但是由于某种原因,它是一个已贬值的转换器,标有红色的“已弃用:该转换器在大多数monad上无效”。
ListT
[两个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
中的Compose
newtype
用于我们的优势:
Data.Functor.Compose
需要一些包装,但是这种解决方案在适当的情况下可能有用:
Data.Functor.Compose