代码之家  ›  专栏  ›  技术社区  ›  MathematicalOrchid

使一元代码更短

  •  9
  • MathematicalOrchid  · 技术社区  · 11 年前

    考虑以下代码:

    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)
    

    现在,假设出于某种原因,我将其更改为在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'
    

    好吧,就这些 作品 还有一切,但是。。。我们能改进一下吗?我有一种挥之不去的感觉,我应该能够定义一些漂亮的中缀函数,让它看起来更漂亮,但每次我试图弄清楚怎么做时,我的大脑都会在一段时间后麻木。。。

    3 回复  |  直到 11 年前
        1
  •  10
  •   Tarmil    11 年前

    你的直觉是对的。这是 ap 中的函数 Monad 类,或 <*> 中的运算符 Applicative 类,几乎所有monad都实现了该类(并且实际上将成为 莫纳德 未来)。

    以下是其类型:

    (<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
    

    所以它基本上应用了一个包装函数 a -> b 到一个包裹 a 返回已包装的 b 。相当于:

    mf <*> mx = do
      f <- mf
      x <- mx
      return $ f x
    

    以下是如何在您的案例中使用它,强调不同案例之间的相似性:

    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
    

    这可以通过考虑以下因素来缩短 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
    

    更进一步,通过使用运算符 <$> 相当于 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
    
        2
  •  2
  •   Cubic mgsloan    11 年前
    transform :: Foo -> State Int Bar
    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
    

    要求 Control.Applicative Functor / Applicative 您的Monad的实例(它们是用于状态的,对于其他Monad来说实现起来相对简单)。

        3
  •  0
  •   MathematicalOrchid    11 年前

    对于任何试图解决这个问题的人。。。

    似乎关键的定义如下:

    mf <*> mx = do
      f <- mf
      x <- mx
      return (f x)
    
    f <$> mx = do
      x <- mx
      return (f x)
    

    特别是,类型不同; <*> mf 虽然 <$> f :

    (<*>) :: Monad m => m (x -> y) -> m x -> m y
    (<$>) :: Monad m =>   (x -> y) -> m x -> m y
    

    (当然,这两个都不是 Monad 方法,甚至是方法。但你明白了……)

    作为一个从不使用 fmap ,这需要一段时间才能适应。。。