haskell - Haskell 中的一元 IO 构造只是一种约定吗?

标签 haskell

关于 Haskell 的单子(monad) IO 构造:

  • 这只是一个约定还是有实现原因?

  • 您能否不直接将 FFI 写入 libc.so 来执行 I/O,并跳过整个 IO-monad 的事情?

  • 它无论如何都会起作用,或者结果是不确定的,因为:

    (a) Haskell 的惰性求值?

    (b) 另一个原因,比如 GHC 对 IO monad 进行模式匹配,然后以特殊方式(或其他方式)处理它?<​​/p>

真正的原因是什么 - 最终你最终使用了副作用,那么为什么不采用简单的方法呢?

最佳答案

是的,monadic I/O 是 Haskell 懒惰的结果。但具体来说,一元 I/O 是 Haskell 纯粹的结果,这对于惰性语言的可预测性来说是非常必要的。

这很容易用一个例子来说明。想象一下 Haskell 并不纯粹,但它仍然很懒。 putStrLn 的类型不是 String -> IO (),而是简单的类型 String -> (),并且会打印作为副作用发送到标准输出的字符串。这样做的问题是,只有在实际调用 putStrLn 时才会发生这种情况,并且在惰性语言中,只有在需要函数结果时才会调用函数。

问题来了:putStrLn 生成 ()。查看 () 类型的值是没有用的,因为 () means “boring” 。这意味着该程序将执行您所期望的操作:

main :: ()
main =
  case putStr "Hello, " of
    () -> putStrLn " world!"

-- prints “Hello, world!\n”

但我认为你会同意编程风格非常奇怪。但是,case ... of 是必要的,因为它强制通过与 () 匹配来评估对 putStr 的调用。如果你稍微调整一下程序:

main :: ()
main =
  case putStr "Hello, " of
    _ -> putStrLn " world!"

…现在它只打印 world!\n,并且第一个调用根本不被评估。

但是,这实际上变得更糟,因为一旦您开始尝试进行任何实际编程,就变得更加难以预测。考虑这个程序:

printAndAdd :: String -> Integer -> Integer -> Integer
printAndAdd msg x y = putStrLn msg `seq` (x + y)

main :: ()
main =
  let x = printAndAdd "first" 1 2
      y = printAndAdd "second" 3 4
  in (y + x) `seq` ()

这个程序打印出first\nsecond\n还是second\nfirst\n?如果不知道 (+) 计算其参数的顺序,我们就不知道。在 Haskell 中,计算顺序甚至并不总是明确定义的,因此完全有可能两个效果的执行顺序实际上完全无法确定!

这个问题在具有明确定义的求值顺序的严格语言中不会出现,但在像 Haskell 这样的惰性语言中,我们需要一些额外的结构来确保副作用 (a) 实际求值和 (b) 执行正确的顺序。 Monad 恰好是一个接口(interface),它优雅地提供了强制执行该顺序所需的结构。

这是为什么呢?这怎么可能呢?嗯,the monadic interface provides a notion of data dependency in the signature for >>= ,它强制执行明确定义的评估顺序。 Haskell 的 IO 实现是“神奇的”,因为它是在运行时实现的,但单子(monad)接口(interface)的选择远非任意。这似乎是一种用纯语言对顺序 Action 的概念进行编码的相当好的方法,并且它使 Haskell 可以在不牺牲可预测的效果顺序的情况下实现惰性和引用透明。

值得注意的是,单子(monad)并不是以纯粹的方式编码副作用的唯一方式——事实上,从历史上看,they’re not even the only way Haskell handled side-effects 。不要误以为 monad 仅用于 I/O(它们不是),仅在惰性语言中有用(即使在严格语言中,它们对于保持纯度也非常有用),仅在纯语言中有用(很多东西都是有用的 monad,不仅仅是为了加强纯度),或者你需要 monad 来执行 I/O(你不需要)。不过,出于这些目的,它们在 Haskell 中似乎确实表现得很好。

<小时/>

† 对此,Simon Peyton Jones 曾指出 “Laziness keeps you honest” with respect to purity .

关于haskell - Haskell 中的一元 IO 构造只是一种约定吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45136398/

相关文章:

haskell - Yesod,如何从 Javascript/Julius 中的 JSON 数据生成类型安全链接

haskell - 将 Data.Constraint.Forall 与等式约束一起使用

algorithm - 检查数值约束表达式的允许值的等价性/子集

haskell - 如何获得 ByteString 的 Ptr?

haskell - 使用 repa 计算图像直方图

haskell - 当使用 Aeson 解析 JSON 时,为什么 Maybe 在类型参数中会受到不同的处理?

haskell - 我可以使用 Haskell 的堆栈来编译并_仅_运行测试吗?

performance - 如何以空间和时间高效的方式填充 Data.Map

Java 接口(interface)和 Haskell 类型类 : differences and similarities?

Haskell 做符号和模式匹配吗?