0

我不知道为什么我会得到两个不同的结果,但我确信它与IO,我开始讨厌了!

例如:

  ghci> x <- readFile "foo.txt"
  ghci> let y = read x :: [Int]
  ghci> :t y
  y :: [Int]

现在,当我创建该文件并执行相同的操作时,它会出现IO [Int]

foo.txt是一个仅包含以下内容的 txt 文件:12345

谁能给我解释一下?当我要拍它的时候!

感谢您的任何见解!

4

4 回答 4

6

阅读有关ghci的信息。去引用

GHCi 提示符下接受的语句语法与 Haskell do 表达式中的语句语法完全相同。但是,这里没有 monad 重载:在提示符下键入的语句必须在 IO monad 中。

基本上,IO当您在 ghci 中编写任何内容时,您都在 Monad 中。

于 2012-09-30T19:01:43.543 回答
5

大意

清楚 HaskellIO中产生值的操作和值本身之间的区别。

推荐阅读

对 Haskell 中 IO 的一个很好的参考是 sigfpe 的The IO Monad for People who Simply Don't Care,它不希望您想知道 monad 的理论基础 。

请注意,这是他假设您不关心的单子理论位。他假设你确实关心做 IO。它不是很长,而且我认为它非常值得您阅读,因为它明确了一些您不知道的“规则”,因此会引起您的不适。

你的代码

无论如何,在你的代码中

x <- readFile "foo.txt"

readFile "foo.txt"位具有 type IO String,这意味着它是一个产生字符串的操作。当你这样做时x <- readFile "foo.txt",你x用来指代它产生的字符串。x注意输出和产生它的操作之间的区别readFile "foo.txt"

接下来我们来看看y。您定义let y = read x :: [Int],因此y是您指定的 Ints 列表。但是,y与定义它的整个块不同。

example = do
  x <- readFile "foo.txt"
  let y = read x :: [Int]
  return y

在这里example :: IO [Int],whiley本身有 type [Int]

你沮丧的原因

如果您来自命令式语言,这首先会令人沮丧 - 您习惯于在任何使用值的地方使用产生值的函数,但您习惯于这些函数也被允许执行任意 IO 操作.

在 Haskell 中,您可以使用“纯”函数(不使用 IO)而不是 IO 操作来做任何您喜欢的事情。Haskell 程序员看到了返回值的 IO 操作(只能在其他 IO 操作中重用)与可以在任何地方使用的纯函数之间的巨大差异。

这意味着您最终可能会一直陷入尴尬的 IO monad 中,并且您的所有函数都充满了 IO 数据类型。这很不方便,而且你写的代码很乱。

如何避免 IO 混乱

首先在不使用外部(文件或用户)数据的情况下完全解决您遇到的问题:

  • 将您通常从文件或用户读取的示例数据写入源代码中的值。
  • 使用定义中所需的任何数据作为函数的参数编写函数。这是编写纯代码时获取数据的唯一方法。先写纯代码。
  • 在样本数据上测试您的功能。(如果你愿意,你可以在每次编写新函数时重新加载 ghci,确保它符合你的预期。)
  • 一旦您的程序在没有 IO 的情况下完成,您可以在最后将其作为纯代码的包装器引入。

这意味着在您的程序中,我认为在您readFile快要完成之前,您不应该编写任何或其他 IO 代码。

这是一个完全不同的工作流程 - 在命令式语言中,您将编写代码来读取数据,然后执行操作,然后写入数据。在 Haskell 中,最好先编写完成工作的代码,然后再编写读取和写入数据的代码,前提是您知道功能是正确的。

于 2012-09-30T22:13:56.120 回答
4

你实际上不能在 Haskell 源文件中做完全相同的事情,所以我怀疑你所做的实际上看起来像这样:

readFoo = do
    x <- readFile "foo.txt"
    let y = read x :: [Int]
    return y

并且惊讶于 的类型readFoo出现IO [Int],即使它返回y的是 type [Int]

如果是这种情况,您困惑的根源在于returnHaskell 不是命令式语言的 return 语句。

return在 Haskell 中是一个函数。一个完全普通的功能。像任何其他函数一样,它接受某种类型的值作为输入,并为您提供某种其他类型的值作为输出。专门针对IO(return可以与任何 monad 一起使用,但我们在这里保持简单) 的情况,它具有以下类型:

a -> IO a

所以它接受任何类型的值,并给你一个包裹在IOmonad 中的相同类型的值。所以如果y有类型[Int],那么return y就有类型IO [Int],这就是你得到的结果readFoo

没有办法[Int]IO [Int]. 这是故意的。的全部意义IO在于,任何依赖于程序“外部”的任何东西的值都只能出现在一个IO x类型中。因此,一个内部读取“foo.txt”并返回其中整数列表的函数一定不可能用 Haskell 编写,否则整个纸牌屋都会倒塌。如果我们看到一个类型为 的函数readFoo :: [Int],就像您尝试编写的那样,那么我们知道它readFoo只能是一个特定的整数列表;它不能是一个依赖于磁盘上文件内容的列表。

于 2012-10-01T02:50:05.343 回答
2

GHCi 做了一点魔法,让在命令提示符下快速测试东西变得更容易。如果这是程序的一部分,那么IO [Int]确实是正确的类型。GHCi 让您将其视为只是Int为了节省您的打字时间。

所以这就是“为什么”。

现在,如果您有一个特定的问题,例如“鉴于类型签名是这样的,我该怎么做 X?”......

于 2012-09-30T19:50:18.887 回答