haskell - 使用 bang 模式时 IO monad 会变得严格吗?

标签 haskell io monads lazy-evaluation syntactic-sugar

我期望以下代码片段:

main = do
    let !x = [2,3,5,2,3,5,6,7,1,3,0,1]
    begin <- getCPUTime
    let !rx = reverse x
    end <- getCPUTime
    putStrLn $ "Calculation time: " ++ show (end - begin) ++ " ps."
    putStrLn $ "Result: " ++ show rx

与以下版本相同:

main = do
    let x = [2,3,5,2,3,5,6,7,1,3,0,1]
    begin <- x `seq` getCPUTime
    let rx = reverse x
    end <- rx `seq` getCPUTime
    putStrLn $ "Calculation time: " ++ show (end - begin) ++ " ps."
    putStrLn $ "Result: " ++ show rx

这是真的吗?在第一个版本中,如果“需要”时 xrx 计算结果为 WHNF,则此值为 false。

顺便说一句,我想提出一种用于深度评估的语法糖,名为“双爆炸模式”。

最佳答案

您提供的特定代码示例生成相同的编译代码。如果您采用以下程序:

{-# LANGUAGE BangPatterns #-}

module Bang where

import System.CPUTime

main1 = do
    let !x = [2,3,5,2,3,5,6,7,1,3,0,1]
    begin <- getCPUTime
    let !rx = reverse x
    end <- getCPUTime
    putStrLn $ "Calculation time: " ++ show (end - begin) ++ " ps."
    putStrLn $ "Result: " ++ show rx

main2 = do
    let x = [2,3,5,2,3,5,6,7,1,3,0,1]
    begin <- x `seq` getCPUTime
    let rx = reverse x
    end <- rx `seq` getCPUTime
    putStrLn $ "Calculation time: " ++ show (end - begin) ++ " ps."
    putStrLn $ "Result: " ++ show rx

并使用(使用 GHC 版本 8.6.5)进行编译:

stack ghc -- -dsuppress-all -dsuppress-uniques -ddump-simpl -fforce-recomp -O2 Bang.hs

您会发现在转储的 GHC 核心中,main1main2 被编译为完全相同的代码,该代码实际上被拉出到单独的 main4功能:

main1
main1 = main4 `cast` <Co:3>

main2
main2 = main4 `cast` <Co:3>

但是,一般来说,let !x = ... 构造并不完全等同于使用 let x = ... 后跟 x `seq` y。例如,以下两个IO Action 是不同的:

foo :: IO ()
foo = do
  let !x = undefined
  return ()

bar :: IO ()
bar = do
  let x = undefined
  return $ x `seq` ()

第一个立即生成异常:

main = do
    print 1
    foo       -- EXCEPTION!
    print 2

第二个执行时不执行任何操作,但如果您尝试仔细检查它的结果,则会生成异常:

main = do
    print 1
    bar        -- does nothing
    print 2
    x <- bar   -- also does nothing
    print 3
    () <- bar  -- EXCEPTION!
    print 4

我相信脱糖后,barfoo相当于:

bar = return (undefined `seq` ())
foo = undefined `seq` return ()

这解释了他们不同的行为。

关于haskell - 使用 bang 模式时 IO monad 会变得严格吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58553006/

相关文章:

haskell - 如何为 lambda 中的运算符赋予不定性?

scala - 使用 sc.textfile 时读取文本文件的是驱动程序还是 worker ?

list - 为什么 Haskell 不允许在推导中进行模式匹配?

haskell - 为什么某些类型类以 "Monad"为前缀?

haskell - Haskell 中的 GUI 编程 - Ubuntu 上的安装问题

haskell - Haskell 中的融合是什么?

function - (->) 的 Profunctor 实例同时定义 dimap 和 lmap/rmap 是否有任何原因?

c# - 如何在尝试删除文件时调试 "Sharing Violation"

django - 如何处理 mod_wsgi/django 中的阻塞 IO?

haskell - 使用 Haskell 状态 monad 有代码味道吗?