Haskell中的monadic IO构造只是一个约定吗? [英] Is the monadic IO construct in Haskell just a convention?

查看:81
本文介绍了Haskell中的monadic IO构造只是一个约定吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Haskell中的monadic IO构造只是一个约定,还是存在实现原因?

您能否将FFI转换为libc.so而不是IO IO,并跳过IO Monad块?

无论如何它会起作用,或者结果是不确定的,因为Haskell评估懒惰或其他东西,比如GHC是IO Monad的模式匹配,然后在特殊的方式或别的什么。



真正的原因是什么?最后你会产生副作用。那么为什么不这样做呢?

解决方案

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



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



麻烦: putStrLn 产生()。查看()类型的值是无用的,因为 ()表示无聊。这意味着这个程序会做你想做的事:

  main ::()
main =

() - >的情况putStrHello, putStrLn世界!
$ b $ - 打印Hello,world!\\\

但是我认为你可以同意编程风格很奇怪。然而,的情况是必须的,因为它通过匹配来强制评估对 putStr 的调用()。如果你稍微调整了这个程序:

  main ::()
main =
case putStrHello ,
- - > putStrLn世界!

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



然而,这实际上变得更糟,因为它变得更加难以只要你开始尝试做任何实际的编程就会预测。考虑这个程序:

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

main ::()
main =
let x = printAndAddfirst1 2
y = printAndAddsecond3 4
in(y + x)`seq`()​​

该程序是否打印出 first \\\
second\\\
second\\\
first\\\
?在不知道(+)评估其参数的顺序的情况下,我们不知道。在Haskell中,评估顺序并不总是定义良好,所以完全有可能两个效果的执行顺序实际上完全不可能确定!


这个问题在严格定义的评估顺序中没有出现严格的语言,但是像Haskell这样的懒惰语言中,我们需要一些额外的结构来确保副作用(a)实际评估和(b)正确执行订购。 Monads碰巧是一个界面,它优雅地提供了执行该命令的必要结构。



为什么是这样?那又如何呢?那么, monadic接口在>> = ,它执行定义明确的评估顺序。 Haskell对 IO 的实现是魔术的,因为它是在运行时实现的,但是monadic接口的选择远非任意。这似乎是一种用纯语言编码顺序操作概念的相当好的方法,它可以让Haskell在没有牺牲可预测的效果排序的情况下变得懒惰和透明地透明。



值得注意的是monad不是纯粹的方式来编码副作用 - 事实上,历史上,它们甚至不是处理 Haskell 处理副作用的唯一方法。不要被误认为单子只是为了I / O(他们不是),只在懒惰的语言中有用(它们对于保持纯度即使用严格的语言也是非常有用的),只在纯语言中有用(很多东西都是有用的单子,不仅仅是为了强制纯度),或者你需要单子做I / O(你不需要)。不过,他们在Haskell中似乎做得非常好。




†关于这点,西蒙佩顿琼斯曾经指出,懒惰让你诚实在纯度方面


Is the monadic IO construct in Haskell just a convention or is there is a implementation reason for it?

Could you not just FFI into libc.so instead to do your IO, and skip the IO Monad pieceg?

Would it work anyway, or is the outcome undeterministic because of Haskell evaluating lazy or something else, like that the GHC is pattern matching for IO Monad and then handling it in a special way or something else.

What is the real reason? In the end you end up in a side effect. So why not do it the simple way?

解决方案

Yes, monadic I/O is a consequence of Haskell being lazy. Specifically, though, monadic I/O is a consequence of Haskell being pure, which is effectively necessary for a lazy language to be predictable.

This is easy to illustrate with an example. Imagine for a moment that Haskell were not pure, but it was still lazy. Instead of putStrLn having the type String -> IO (), it would simply have the type String -> (), and it would print a string to stdout as a side-effect. The trouble with this is that this would only happen when putStrLn is actually called, and in a lazy language, functions are only called when their results are needed.

Here’s the trouble: putStrLn produces (). Looking at a value of type () is useless, because () means "boring". That means that this program would do what you expect:

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

-- prints "Hello, world!\n"

But I think you can agree that programming style is pretty odd. The case ... of is necessary, however, because it forces the evaluation of the call to putStr by matching against (). If you tweak the program slightly:

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

…now it only prints world!\n, and the first call isn’t evaluated at all.

This actually gets even worse, though, because it becomes even harder to predict as soon as you start trying to do any actual programming. Consider this program:

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` ()

Does this program print out first\nsecond\n or second\nfirst\n? Without knowing the order in which (+) evaluates its arguments, we don’t know. And in Haskell, evaluation order isn’t even always well-defined, so it’s entirely possible that the order in which the two effects are executed is actually completely impossible to determine!

This problem doesn’t arise in strict languages with a well-defined evaluation order, but in a lazy language like Haskell, we need some additional structure to ensure side-effects are (a) actually evaluated and (b) executed in the correct order. Monads happen to be an interface that elegantly provide the necessary structure to enforce that order.

Why is that? And how is that even possible? Well, the monadic interface provides a notion of data dependency in the signature for >>=, which enforces a well-defined evaluation order. Haskell’s implementation of IO is "magic", in the sense that it’s implemented in the runtime, but the choice of the monadic interface is far from arbitrary. It seems to be a fairly good way to encode the notion of sequential actions in a pure language, and it makes it possible for Haskell to be lazy and referentially transparent without sacrificing predictable sequencing of effects.

It’s worth noting that monads are not the only way to encode side-effects in a pure way—in fact, historically, they’re not even the only way Haskell handled side-effects. Don’t be misled into thinking that monads are only for I/O (they’re not), only useful in a lazy language (they’re plenty useful to maintain purity even in a strict language), only useful in a pure language (many things are useful monads that aren’t just for enforcing purity), or that you needs monads to do I/O (you don’t). They do seem to have worked out pretty well in Haskell for those purposes, though.


† Regarding this, Simon Peyton Jones once noted that "Laziness keeps you honest" with respect to purity.

这篇关于Haskell中的monadic IO构造只是一个约定吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆