请考虑以下代码:
transform :: Foo -> Bar
transform foo =
case foo of
Foo1 x -> Foo1 x
Foo2 x y -> Foo2 x (transform y)
Foo3 x y z -> Foo3 x (transform y) (transform z)
Run Code Online (Sandbox Code Playgroud)
现在假设由于某种原因我改变它以在monad中工作(例如,因为我有状态我想随身携带或其他).现在我们有
transform :: Foo -> State Int Bar
transform foo =
case foo of
Foo1 x -> return $ Foo1 x
Foo2 x y -> do
y' <- transform y
return $ Foo2 x y'
Foo3 x y z -> do
y' <- transform y
z' <- transform z
return $ Foo3 x y' z'
Run Code Online (Sandbox Code Playgroud)
好吧,所有的工作和一切,但......我们可以改善这一点吗?我有一种唠叨的感觉,我应该能够定义一些漂亮的中缀功能,以使这看起来更好,但每次我试图弄清楚如何,我的思绪在一段时间后麻木...
Tar*_*mil 10
你的直觉是正确的.这是ap函数在Monad类中的作用,或者等同于类中的<*>运算符Applicative,几乎所有monad都实现(并且实际上将成为Monad将来的超类).
这是它的类型:
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)
所以它基本上将一个包装函数a -> b应用于包装a以返回一个包装b.它相当于:
mf <*> mx = do
f <- mf
x <- mx
return $ f x
Run Code Online (Sandbox Code Playgroud)
以下是如何在您的情况下使用它,强调不同情况之间的相似性:
transform foo =
case foo of
Foo1 x -> return Foo1 <*> return x
Foo2 x y -> return Foo2 <*> return x <*> transform y
Foo3 x y z -> return Foo3 <*> return x <*> transform y <*> transform z
Run Code Online (Sandbox Code Playgroud)
考虑到这一点,可以缩短这一点return f <*> return x == return (f x):
transform foo =
case foo of
Foo1 x -> return $ Foo1 x
Foo2 x y -> return (Foo2 x) <*> transform y
Foo3 x y z -> return (Foo3 x) <*> transform y <*> transform z
Run Code Online (Sandbox Code Playgroud)
甚至进一步,通过使用操作<$>这相当于fmap从Functor类:
transform foo =
case foo of
Foo1 x -> return $ Foo1 x
Foo2 x y -> Foo2 x <$> transform y
Foo3 x y z -> Foo3 x <$> transform y <*> transform z
Run Code Online (Sandbox Code Playgroud)