为什么将IO结果包装在IO Monad中

Fai*_*lde 1 monads haskell

在Haskell中我们有一个功能readFile :: FilePath -> IO String.理解monad时我的问题是为什么要包装它IO?难道我们不能只写这样的函数:

(lines.readFile) path
Run Code Online (Sandbox Code Playgroud)

而不是

(readFile >>= lines) path 
Run Code Online (Sandbox Code Playgroud)

IO包装器提供什么好处?

arr*_*owd 6

Haskell表达式是引用透明的.这意味着如果readFile真的有一个类型FilePath -> String,那么表达式readFile "a.txt"总会产生相同的结果.即使您读取文件,然后更改它,然后再次阅读,您将获得处于其第一状态的内容.

因此,我们需要在价值观行动之间进行斗争,这IO就是为了什么.readFile "a.exe"执行与之关联的操作之前,它不允许您在其他表达式中使用结果.因此,在更改文件后,您必须再次执行读取操作,以获取文件内容,因此您将能够看到更改.

  • 我真的不喜欢这个答案的措辞......你说的是因为表达式是引用透明的,所以`readFile"a.txt"`总是会返回相同的结果......这听起来很糟糕的语言设计观点看法.该语言被设计*以避免表达式*中的I/O操作以获得*引用透明度...我的意思是:引用透明度是*不*"原因",它是通过强制I/O实现的目标IO monad.Haskell*可能已经被设计为OP想要的,但是它不会被引用透明. (3认同)

Cha*_*Rex 5

我们编写创建计算机程序的函数

应该注意的是,Haskell是一种函数式编程语言.在数学意义上,函数总是为相同的输入生成相同的值.

现在这个总是产生相同结果的要求会对事物产生很大的限制,因为读取文件的函数每次都必须产生相同的结果,即使文件稍后被更改.这显然不是我们真正想要的.

但是,有一种方法可以使函数式编程语言能够处理读取更改的文件.你所做的就是编写一个能产生计算机应该执行的动作的函数.因此,您可以执行由以下步骤组成的操作:

Read the file
Break it into lines
Change the even-numbered lines to uppercase
Output the lines to the screen
Run Code Online (Sandbox Code Playgroud)

这四项行动尚未执行.它们只是我们可能执行的一系列操作.函数可以在每次调用时返回完全相同的潜在动作序列,这使其成为一个合适的数学函数.

main :: IO aHaskell中的函数返回程序应执行的操作.它总是返回相同的动作,使其成为一个合适的数学函数.运行程序时,计算机会评估该main功能,产生计算机应执行的操作,然后计算机执行该操作.

记谱法

记谱法将过程中的陌生感带走,让您感受到更加标准的编程语言.你有三个选择:

  1. 执行操作并对其结果不执行任何操作
  2. 执行操作并存储其结果
  3. 仅使用函数处理数据(无操作)

这些分别按以下方式完成:

  1. action args
  2. result <- action args
  3. let result = f . g . h . whateverCalculation $ value

这类似于你所做的像C这样的命令式语言:

  1. action(args);
  2. result = action(args);
  3. result = f(g(h(whateverCalculation(value))));

  • `main`不是一个函数.它的类型没有箭头. (4认同)
  • @DavidTonhofer这是一个类型为"IO a"的值.如果你想要一个更生动的描述,它是一个*程序*,在这里的答案解释的意义上.(说'IO a`类型的值是一个程序与说例如`[a]`类型的值是一个列表没什么不同.) (3认同)