haskell - IO monad防止嵌入式mapM短路?

标签 haskell lazy-evaluation traversal io-monad strictness

下面的代码有点神秘。在问题的非玩具版本中,我试图在 monad Result 中进行 monadic 计算,其值只能从 IO 中构造。似乎 IO 背后的魔力使此类计算变得严格,但我无法弄清楚这是如何发生的。

编码:

data Result a = Result a | Failure deriving (Show)

instance Functor Result where
  fmap f (Result a) = Result (f a)
  fmap f Failure = Failure

instance Applicative Result where
  pure = return
  (<*>) = ap

instance Monad Result where
  return = Result
  Result a >>= f = f a
  Failure >>= _ = Failure

compute :: Int -> Result Int
compute 3 = Failure
compute x = traceShow x $ Result x

compute2 :: Monad m => Int -> m (Result Int)
compute2 3 = return Failure
compute2 x = traceShow x $ return $ Result x

compute3 :: Monad m => Int -> m (Result Int)
compute3 = return . compute

main :: IO ()
main = do
  let results = mapM compute [1..5]
  print $ results
  results2 <- mapM compute2 [1..5]
  print $ sequence results2
  results3 <- mapM compute3 [1..5]
  print $ sequence results3
  let results2' = runIdentity $ mapM compute2 [1..5]
  print $ sequence results2'

输出:
1
2
Failure
1
2
4
5
Failure
1
2
Failure
1
2
Failure

最佳答案

不错的测试用例。这是正在发生的事情:

  • mapM compute像往常一样,我们看到工作中的懒惰。这里没有惊喜。
  • mapM compute2我们在 IO monad 内部工作,它的 mapM定义将需要整个列表:不像 Result一旦 Failure 跳过列表的尾部已找到,IO将始终扫描整个列表。注意代码:
    compute2 x = traceShow x $ return $ Result x
    

    因此,只要访问了 IO 操作列表的每个元素,上述内容就会打印调试消息。都是,所以我们打印所有内容。
  • mapM compute3我们现在大致使用:
    compute3 x = return $ traceShow x $ Result x
    

    现在,自从 return在 IO 是惰性的,它不会触发 traceShow返回 IO 操作时。所以,当 mapM compute3正在运行,没有看到任何消息。相反,我们仅在 sequence results3 时才看到消息。运行,这会强制 Result - 不是所有的,但只有需要的。
  • 决赛 Identity例子也很棘手。请注意:
    > newtype Id1 a = Id1 a
    > data Id2 a = Id2 a
    > Id1 (trace "hey!" True) `seq` 42
    hey!
    42
    > Id2 (trace "hey!" True) `seq` 42
    42
    

    使用 newtype 时,在运行时不涉及装箱/拆箱(AKA 提升),因此强制 Id1 x值(value)导致x被迫。与 data类型这不会发生:值被包装在一个盒子里(例如 Id2 undefined 不等于 undefined )。

    在您的示例中,您添加了 Identity构造函数,但来自 newtype Identity !!所以,打电话的时候
    return $ traceShow x $ Result x
    
    return这里不包装任何东西,traceShow立即触发 mapM正在运行。
  • 关于haskell - IO monad防止嵌入式mapM短路?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37538532/

    相关文章:

    c++ - 使用基于范围的 for 循环自定义容器遍历

    c - 二叉搜索树的非递归迭代器

    haskell - 警告 : newtype `CInt' is used in an FFI declaration,

    ios - 如何防止在 Swift 中延迟加载静态变量

    haskell - null 而不是 ==

    ruby - ruby 中的方法链接

    haskell - 为什么惰性评估有用?

    java - 在矩阵中寻找路径的深度优先搜索

    haskell - Cabal中的变量(Haskell)

    haskell - 管理大型 Haskell 记录的更好方法是什么?