liftM2的懒惰版本

时间:2018-06-10 12:42:02

标签: haskell monads lazy-evaluation

回答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 m2IO中)不同。

有没有办法用与liftM2Lazy相同的类型编写liftM2(或者在某种程度上更受限制?),这样可以保留一般的懒惰?所以,例如liftM2Lazy (&&)无法区分andMliftM2Lazy (||)来自orM(有明显的定义)等等?

3 个答案:

答案 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