如何将 all 与 monadic 函数一起使用?

How to use all with a monadic function?

我发现自己处于一种情况,我想将 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

是否有 Better™ 方法来做到这一点?

如果您已经在使用 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

如果你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

我认为大多数人会更愿意看到最后两个版本中的一个,尽管最后一个版本对于说英语的人来说有点奇怪 "if and foo then bar else baz"