IO Monad中的短路和

时间:2014-11-23 18:07:28

标签: haskell

我知道问题已被提出there,但我无法相信没有直接答案。

我理解将副作用隐藏在(&&)中是不好的,但在我的情况下,副作用只是检查过时的世界(存在文件,检查修改时间等,询问用户)是/否的问题。

那么haskell的方法是什么样的,所以如果cond1为false,则不会执行cond2。

cond1, cond2 :: IO bool


main = do
   cond <- liftM2 (&&) con1 con2
   if cond
   then   result1
   else   result2

我期待像cond <- all [con1, con2]或类似的东西,但我找不到任何东西。

更新

我可以看到很多手动解决方案。我仍然感到疑惑,这个功能在某处并不存在。 模糊评估的一个优点是它不仅像C中的硬编码&&那样短路。在命令式模式下,Haskell甚至不能短路时真的很奇怪-circuit &&。 虽然,所有解决方案都以某种方式使用,如果短路评估。有没有办法制作一个通用的lazzy liftM2

5 个答案:

答案 0 :(得分:8)

Pipes.Prelude.and这就是False所做的事情,如果它们中的任何一个是import Pipes (each) import qualified Pipes.Prelude as Pipes conds :: [IO Bool] conds = ... main = do cond <- Pipes.and (each conds >-> Pipes.sequence) print cond ,那么它会通过有效生成的条件和短路的惰性流:

{{1}}

相关链接:

答案 1 :(得分:4)

这与其他人的说法没有什么不同,但仅仅模仿and的定义并不是最简单的:

 andM = foldr (&&&) (return True)
  where ma &&& mb = ma >>= \p -> if p then mb else return p
然后我们得到,说:

 > let test1 = putStrLn "This test succeeds" >> return True
 > let test2 = putStrLn "This test fails" >> return  False
 > andM [test1,test2,undefined,undefined]
 This test succeeds
 This test fails
 False

如果andM没有“短路”,那么未定义的单元格将被评估并返回异常。

liftM2 (&&)无法正常工作,这有点令人恼火。

编辑:我刚才注意到,正如人们可能预期的那样,这是在monad-loopshttp://hackage.haskell.org/package/monad-loops-0.4.2.1/docs/src/Control-Monad-Loops.html#andM

中定义的

答案 2 :(得分:3)

您想要的操作很容易明确定义。

shortCircuitAnd :: Monad m => m Bool -> m Bool -> m Bool
shortCircuitAnd x y = do
   r1 <- x -- perform effect of x
   if r1 
     then y -- perform effect of y and return result
     else return False -- do *not* evaluate or perform effect of y

当然,你也可以使用这个函数中缀,使用反引号:

x `shortCircuitAnd` y == shortCircuitAnd x y

答案 3 :(得分:3)

我们可以使用像MaybeT这样的monad变换器,它是MonadPlus的一个实例。我们的想法是使用guardFalse结果转换为mzero,这将停止计算。然后我们将生成的Maybe转换回Bool

import Control.Monad.Trans
import Control.Monad.Trans.Maybe

effyand :: (Functor m, Monad m) => [m Bool] -> m Bool
effyand = fmap isJust . runMaybeT . mapM_ (lift >=> guard)

答案 4 :(得分:3)

我定义了像

这样的幺半群
newtype AllM m = AllM { getAllM :: m Bool }

instance (Monad m) => Monoid (AllM m) where
    mempty = AllM (return True)
    mappend (AllM u) (AllM v) = AllM $
        u >>= \x -> if x then v else return False

然后getAllM . mconcat . map AllM $ [con1, con2]