IO和也许monad互动

时间:2011-10-07 18:28:02

标签: haskell

我有以下代码,但我觉得它太丑陋而且势在必行。有人会说它更具功能吗? (我和MaybeT混淆但是无法使它工作)也欢迎应用答案。

getString :: IO String

pred :: String -> Bool

f :: String -> String

result :: IO (Maybe String)
result = do
  s <- getString
  if pred s
    then return $ Just $ f s
    else return Nothing

编辑:一个后续问题:如果pred和f都在IO中返回结果会怎么样(我应该把它拆分成一个单独的问题吗?)

getString :: IO String

pred :: String -> IO Bool

f :: String -> IO String

result :: IO (Maybe String)
result = do
  s <- getString
  b <- pred s
  if b
    then Just <$> f s
    else return Nothing

5 个答案:

答案 0 :(得分:26)

我首先从IO monad中取出逻辑。然后您的函数可以写为

result :: IO (Maybe String)
result = foo <$> getString

foo :: String -> Maybe String
foo s | pred s    = Just (f s)
      | otherwise = Nothing 

您可以使用一些花哨的组合器以不同的方式编写foo,但我不认为这是必要的。最重要的是让你的逻辑超出IO,以便更容易测试。

答案 1 :(得分:10)

这是一个很好的小组合器:

ensure :: MonadPlus m => (a -> Bool) -> (a -> m a)
ensure p x = guard (p x) >> return x

现在我们可以编写一个纯函数来检查你的谓词并在适当的时候应用f

process :: String -> Maybe String
process = fmap f . ensure pred

将此提升为IO操作只是另一个fmap

result = fmap process getString

就个人而言,我可能会内联process,并以这种方式写下来:

result = fmap (fmap f . ensure pred) getString

...这是对正在发生的事情的一个相对清晰的描述。

答案 2 :(得分:9)

代码的明显转换是return操作的因素:

result = do
  s <- getString
  return $ if pred s
           then Just (f s)
           else Nothing

这使得模式更加明显:

result = liftM g getString
g s | pred s    = Just (f s)
    | otherwise = Nothing

通过从外部应用f,我们可以使下一个模式显而易见:

g s = liftM f $ if pred s then Just s else Nothing

让我们重新加入if块:

g = liftM f . mfilter pred . return

总结:

result = liftM (liftM f . mfilter pred . return) getString

答案 3 :(得分:5)

import Control.Monad

result = getString >>= (return . fmap f . (mfilter pred . Just) )

答案 4 :(得分:3)

你不能轻易摆脱笨重的if-then-else,但你可以逃脱冗余的returns

import Control.Monad

result :: IO (Maybe String)
result = go <$> getString where
  go s | pred s    = Just $ f s
       | otherwise = Nothing