我发现自己处于一种我希望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
有没有更好的方法来做到这一点?
答案 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.Applicative
和Data.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>