如何使用具有monadic功能的all?

时间:2015-03-11 14:23:52

标签: list haskell monads

我发现自己处于一种我希望all使用monadic函数的情况。在我看来,这并不是太漂亮了:

f :: Monad m => a -> m Bool

g :: Monad m => [a] -> m Int
g xs = do cnd <- liftM (all (== True)) $ mapM f xs
          if cnd
           then return 42
           else return 0

有没有更好的方法来做到这一点?

2 个答案:

答案 0 :(得分:5)

如果您已经使用do - 符号,我根本不会打扰liftM。只是去

g xs = do cnds <- mapM f xs
          return $ if and cnds
                     then 42
                     else 0

或者,如果你想要一个不会运行所有monad的懒惰all,我认为你需要自己编写它。

allM f xs = foldr (\x acc -> do b <- f x; if b then return True else acc) (return True) xs

g = fmap (\cnd -> if cnd then 42 else 0) . allM f

-- much nicer with `bool`:

allM f = foldr (\x acc -> f x >>= bool (return True) acc) (return True)
g = fmap (bool 42 0) . allM f

答案 1 :(得分:5)

如果您import Control.ApplicativeData.Bool(如果使用base >= 4.7),那么您可以将其写为

g xs = bool 0 42 <$> and <$> mapM f xs
-- Or equivalently
-- g xs = bool 0 42 . and <$> mapM f xs
-- g = fmap (bool 0 42 . and) . mapM f

但我不认为这会让你获益匪浅。相反,您也可以将return拉出if-then-else

g xs = do cnd <- and <$> mapM f xs
          return $ if cnd then 42 else 0

甚至

g xs = do ys <- mapM f xs
          return $ if and ys then 42 else 0

我认为最后两个版本中的一个是大多数人会更容易看到的,尽管最后一个版本对于说英语的人而言看起来有点奇怪&#34; if和foo然后禁止baz&#34; < / p>