使monadic代码更短

Mat*_*hid 9 monads haskell

请考虑以下代码:

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)

甚至进一步,通过使用操作<$>这相当于fmapFunctor类:

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)

  • 从技术上讲是的,但由于几乎每个库都为他们的`Monad'定义了一个`Functor`实例,因此避免使用`Functor`方法没有多大意义."适用"几乎一样. (3认同)