【问题标题】:How to use all with a monadic function?如何将 all 与一元函数一起使用?
【发布时间】:2015-03-11 14:23:51
【问题描述】:

我发现自己处于想将all 与一元函数一起使用的情况。在我看来,这并不是很漂亮:

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

有更好的™ 方法吗?

【问题讨论】:

  • 首先,您不想使用all (== True),而是使用and :-)
  • 确实,更一般地说:布尔值的相等比较几乎总是不必要的。如果不是and,你仍然可以写all id
  • 您的实现没有正确“短路”:当纯计算短路时,它必须首先运行所有副作用。请参阅 monad-loops 包(及其兄弟,allM)中的 andM,以了解仅执行尽可能多的副作用以了解答案的版本。

标签: list haskell monads


【解决方案1】:

如果你import Control.ApplicativeData.Bool(如果使用base &gt;= 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”的说英语的人来说看起来有点奇怪

【讨论】:

  • 当您介绍 Control.Applicative 时:值得注意的是,该函数似乎不需要 Monad 约束 - Applicative 足以满足 g 所做的事情(使用 traverse 代替mapM)。因此,即使f 需要Monad,最好将g 中的运算符保持在更基础的级别。
  • @leftaroundabout monad 约束来自f 而不是mapM。有可能f 的约束可以放宽到Applicative f =&gt; a -&gt; f Bool,但目前未知。
  • and &lt;$&gt; mapM f xs 真的是我想要的,太好了!
  • +1 表示 bool 函数,但我认为 bool 0 42 &lt;$&gt; and &lt;$&gt; mapM f xs 确实令人困惑,因为它在两个不同的 Functor 实例上使用了 &lt;$&gt;
  • @Bergi Gotcha。不过,您可以很容易地使用等式推理在两者之间进行转换,但它确实按照我编写的方式使用了两个函子实例。老实说,我不介意这种风格,因为它看起来就像你只是用 &lt;$&gt; 替换 $
【解决方案2】:

如果您已经在使用do-notation,我根本不会打扰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

【讨论】:

    猜你喜欢
    • 2011-01-15
    • 2010-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-20
    • 2019-11-21
    相关资源
    最近更新 更多