【问题标题】:Haskell simplification technique and monadsHaskell 简化技术和单子
【发布时间】:2013-01-11 05:40:57
【问题描述】:

如何可以减少/简化此代码(或一般来说,具有多个输入的代码)?

do
  sex    <- askSex
  name   <- askName
  sayHello sex name

在这种情况下已经很短了,但是当它到达多个输入时,它看起来很乱。有没有办法做类似的事情:

sayHello askSex askName

?

【问题讨论】:

  • 这段代码在我看来非常简单
  • 比这更短的东西很可能很奇怪。如果您要求大量相同类型的输入,您可能会映射这些操作,但在这种情况下不会。
  • 查看Control.Monad中的函数,特别是liftM2
  • 正如 sabauma 所说,使用liftM2。它是您原始代码的抽象,正是您想要的。
  • n.b.如果您在 do-block 的开头包含 do 会更清楚。

标签: haskell


【解决方案1】:

如果你

import Control.Applicative  -- for <$> and <*>
import Control.Monad        -- for join

你可以写

join $ sayHello <$> askSex <*> askName

对于您的示例,您只获取两个参数,这不是一个很大的胜利。但是对于更多的参数它可以使代码更清晰。

join $ doSomething <$> getFirst <*> getSecond <*> getThird <*> getForth

【讨论】:

  • 很遗憾成语方括号没有进入 Haskell:join (|doSomething getFirst getSecond getThird getForth|) 会更好。
  • 嗯,总有the quasiquoter
【解决方案2】:

这是 Applicative Functor 的美好时光:

import Control.Applicative -- at the top of your module before any functions

hello "Male" name = "Hi, " ++ name ++ ", that's a cool name."
hello "Female" name = "Hello, " ++ name ++ ", that's a pretty name."

greet = hello <$> askSex <*> askName >>= putStrLn

它有点像我今天给你的先前答案中的fmap,但对于更多的论点,就像你在这里一样。

将我的hello 之类的函数与应用函子一起使用可以帮助您将 IO 代码与其他代码分开,这是非常好的做法。尝试每次都写hello而不是sayHello

【讨论】:

    【解决方案3】:

    令人讨厌的是,hoogle 对此并没有一个简单的答案。这将被称为bind2。如果它是一个只有一个输入的函数,那么您可以使用=&lt;&lt;,我称之为bind1 的中缀版本。

    sayHello =<< askName
    

    但是对于多个输入,我们就不走运了。无论出于何种原因,标准库都不提供此功能:

    bind2 :: Monad m => (a -> b -> m c) -> m a -> m b -> m c
    bind2 f mx my = do
      x <- mx
      y <- my
      f x y
    
    ...
    
    bind2 sayHello askSex askName
    

    当然,您可以自己定义。

    【讨论】: