回答another question,我写了
andM :: (Monad m) => m Boolean -> m Boolean -> m Boolean
andM m1 m2 = do
x <- m1
case x of
True -> m2
False -> return False
如果没有必要,不评估m2
,与liftM2 (&&) m1 m2
(IO
中)不同。
有没有办法用与liftM2Lazy
相同的类型编写liftM2
(或者在某种程度上更受限制?),这样可以保留一般的懒惰?所以,例如liftM2Lazy (&&)
无法区分andM
,liftM2Lazy (||)
来自orM
(有明显的定义)等等?
答案 0 :(得分:4)
不,这通常是不可能的 - 它需要源到源的转换来将惰性函数提升为惰性monadic函数。
对于IO
具体而言,并且事先知道哪个参数是一个函数是懒惰的(以及它是多么“深度”懒惰 - 也就是说,返回的距离有多远结构一需要评估以发现是否需要执行其他操作),可以使用IO
的异常捕获和unsafeInterleave
功能来编写通用提升函数。但是这些功能非常具体,而且很容易错误使用,我怀疑你最好不要写它们。
答案 1 :(得分:3)
对于一般的monad来说这是不可能的,但是对于IO
的特定情况,使用unsafeInterleaveIO
可以非常容易地(并且相对安全地)实现它,这会使IO动作变得懒惰:
import System.IO.Unsafe
liftIO2Lazy :: (a -> b -> c) -> IO a -> IO b -> IO c
liftIO2Lazy f io1 io2 = do
x <- unsafeInterleaveIO io1
y <- unsafeInterleaveIO io2
return $ f x y
结果将是完全相同的参数,其中f
是惰性的,所以它甚至适用于不遵循相同的从左到右的短路逻辑的函数{ {1}}和&&
:
||
答案 2 :(得分:0)
使用spoon的方法,这有点作弊:
liftM2Lazy f m1 m2 =
case teaspoon $ f undefined undefined of
Just res -> return res
Nothing ->
do x1 <- m1
case teaspoon $ f x1 undefined of
Just res -> return res
Nothing ->
do x2 <- m2
return $ f x1 x2