【问题标题】:Use `seq` and print in haskell使用`seq`并在haskell中打印
【发布时间】:2017-02-02 04:57:06
【问题描述】:

我知道这有点棘手,但我想知道为什么它不起作用!

module Main where

sillyDebug :: Int -> Int -> Int
sillyDebug x y =
    (print x) `seq` (x + y)

main :: IO ()
main = do
    print (sillyDebug 1 2)

虽然它的理想是一样的

sillyDebug = (trace (show x) False) `seq` (x + y)

它与haskell中的惰性评估或副作用有关吗? https://hackhands.com/lazy-evaluation-works-haskell/

【问题讨论】:

  • 请参阅stackoverflow.com/help/mcve。你的大部分进口都是无关紧要的,在这里。 sillyIO 无处定义。 sillyDebug = (trace (show x) False) `seq` (x + y) 甚至无法编译。并描述您面临的问题。你期待什么?你得到了什么?
  • 对不起,这是我的错:3
  • 这个问题是关于 evaluating 一个表达式和 executing 一个 monadic 动作之间的区别,但我认为关于 @987654328 也值得一提@。 a `seq` b 并不完全强制评估 a。它会在 thunk 之间生成依赖关系,因此 if b 被评估,那么 a 也被评估。 print (snd (x `seq` y, z)) 从不评估 x 因为它从不评估 y

标签: debugging haskell io monads seq


【解决方案1】:

仅仅评估一些IO 操作根本没有任何作用。你可以认为IO 有点像 Haskell 可以 做的所有可能的副作用的一个非常大的总和类型,即使它实际上根本没有像那样实现。像这样的:

data IO a where
  PutStrLn :: String -> IO ()
  ReadFile :: FilePath -> IO String
  ExitWith :: ExitCode -> IO a
  ...

此理论可视化中的IO 构造函数之一是Sequence 构造函数,其类型签名如下:

  Sequence :: IO a -> (a -> IO b) -> IO b

此构造函数用于为IO 类型实现>>=

GHC 内部有一个名为magicallyExecuteIO 的神奇函数,其类型为IO a -> a,它协调每个动作以实际执行其相应的副作用。 (顺便说一下,这个函数有时也读作 unsafePerformIO。)GHC 隐式调用 magicallyExecuteIO 对程序的 main 函数的结果,以及用 GHCi 编写的表达式。

但是,如果不使用magicallyExecuteIO,评估IO 构造函数之一,如PutStrLn 不会做任何事情。在这个实现中,它就像任何其他数据构造函数一样工作:

ghci> Just (PutStrLn "hello!")
Just (PutStrLn "hello!") :: Maybe (IO ())

(我用Just 包裹它以防止GHCi 运行IO 操作。)

当然,GHC 的实际 IO 类型不是这样实现的,但这实际上只是一个实现细节。评估 IO a 值不会导致副作用发生,就像评估 Maybe a 值一样。只有magicallyExecuteIO 可以做到这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多