我不知道为什么我会得到两个不同的结果,但我确信它与IO
,我开始讨厌了!
例如:
ghci> x <- readFile "foo.txt"
ghci> let y = read x :: [Int]
ghci> :t y
y :: [Int]
现在,当我创建该文件并执行相同的操作时,它会出现IO [Int]
?
foo.txt
是一个仅包含以下内容的 txt 文件:12345
谁能给我解释一下?当我要拍它的时候!
感谢您的任何见解!
我不知道为什么我会得到两个不同的结果,但我确信它与IO
,我开始讨厌了!
例如:
ghci> x <- readFile "foo.txt"
ghci> let y = read x :: [Int]
ghci> :t y
y :: [Int]
现在,当我创建该文件并执行相同的操作时,它会出现IO [Int]
?
foo.txt
是一个仅包含以下内容的 txt 文件:12345
谁能给我解释一下?当我要拍它的时候!
感谢您的任何见解!
阅读有关ghci的信息。去引用
GHCi 提示符下接受的语句语法与 Haskell do 表达式中的语句语法完全相同。但是,这里没有 monad 重载:在提示符下键入的语句必须在 IO monad 中。
基本上,IO
当您在 ghci 中编写任何内容时,您都在 Monad 中。
清楚 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 数据类型。这很不方便,而且你写的代码很乱。
首先在不使用外部(文件或用户)数据的情况下完全解决您遇到的问题:
这意味着在您的程序中,我认为在您readFile
快要完成之前,您不应该编写任何或其他 IO 代码。
这是一个完全不同的工作流程 - 在命令式语言中,您将编写代码来读取数据,然后执行操作,然后写入数据。在 Haskell 中,最好先编写完成工作的代码,然后再编写读取和写入数据的代码,前提是您知道功能是正确的。
你实际上不能在 Haskell 源文件中做完全相同的事情,所以我怀疑你所做的实际上看起来像这样:
readFoo = do
x <- readFile "foo.txt"
let y = read x :: [Int]
return y
并且惊讶于 的类型readFoo
出现IO [Int]
,即使它返回y
的是 type [Int]
。
如果是这种情况,您困惑的根源在于return
Haskell 不是命令式语言的 return 语句。
return
在 Haskell 中是一个函数。一个完全普通的功能。像任何其他函数一样,它接受某种类型的值作为输入,并为您提供某种其他类型的值作为输出。专门针对IO
(return
可以与任何 monad 一起使用,但我们在这里保持简单) 的情况,它具有以下类型:
a -> IO a
所以它接受任何类型的值,并给你一个包裹在IO
monad 中的相同类型的值。所以如果y
有类型[Int]
,那么return y
就有类型IO [Int]
,这就是你得到的结果readFoo
。
没有办法[Int]
从IO [Int]
. 这是故意的。的全部意义IO
在于,任何依赖于程序“外部”的任何东西的值都只能出现在一个IO x
类型中。因此,一个内部读取“foo.txt”并返回其中整数列表的函数一定不可能用 Haskell 编写,否则整个纸牌屋都会倒塌。如果我们看到一个类型为 的函数readFoo :: [Int]
,就像您尝试编写的那样,那么我们知道它readFoo
只能是一个特定的整数列表;它不能是一个依赖于磁盘上文件内容的列表。
GHCi 做了一点魔法,让在命令提示符下快速测试东西变得更容易。如果这是程序的一部分,那么IO [Int]
确实是正确的类型。GHCi 让您将其视为只是Int
为了节省您的打字时间。
所以这就是“为什么”。
现在,如果您有一个特定的问题,例如“鉴于类型签名是这样的,我该怎么做 X?”......