我知道问题已被提出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
?
答案 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-loops
包http://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
的一个实例。我们的想法是使用guard
将False
结果转换为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]
。