我无法弄清楚为什么我会得到两个不同的结果,但我确定它与我开始讨厌的IO
有关!
例如:
ghci> x <- readFile "foo.txt"
ghci> let y = read x :: [Int]
ghci> :t y
y :: [Int]
现在,当我创建该文件并执行相同的操作时,它会显示为IO [Int]
?
foo.txt
是一个仅包含此内容的文本文件:12345
有人可以向我解释一下吗?因为我要抢购它!
感谢您的任何见解!
答案 0 :(得分:6)
了解ghci。引用
GHCi提示符下接受的语句的语法与Haskell do表达式中语句的语法完全相同。但是,这里没有monad重载:在提示符下键入的语句必须在IO monad中。
当你在ghci中写任何东西时,基本上你都在IO
Monad里面。
答案 1 :(得分:5)
明确Haskell在产生值的IO
操作与值本身之间的区别。
在Haskell中对IO的一个很好的参考,不希望你想知道monad的理论基础是sigfpe的 The IO Monad for People who Simply Don't Care。
请注意,这是他假设你不关心的monad理论位。他假设你关心做IO。 它不是很长,而且我认为这对你来说非常值得一读,因为它明确了一些你不知道的“规则”,因此引起了你的不满。
无论如何,在你的代码中
x <- readFile "foo.txt"
readFile "foo.txt"
位的类型为IO String
,这意味着它是一个产生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]
,而y
本身的类型为[Int]
。
如果你来自一种命令式的语言,那么起初这是令人沮丧的 - 你已经习惯了能够使用在你使用值的地方产生值的函数, 但是你已经习惯了那些允许执行任意IO操作的函数。
在Haskell中,您可以使用“纯”函数(不使用IO)而不是IO操作来执行任何您喜欢的操作。 Haskell程序员看到返回值的IO操作之间存在差异, 只能在其他IO操作中重用,以及可以在任何地方使用的纯函数。
这意味着你最终可能会被困在笨拙的IO monad中, 并且您的所有函数都充满了IO数据类型。这很不方便,你写的代码很乱。
首先使用外部(文件或用户)数据完全解决没有的问题:
这意味着在你的程序中,我认为你不应该写任何readFile
或其他IO代码,直到你差不多完成。
这是一个完全不同的工作流程 - 在命令式语言中,您编写代码来读取数据,然后执行操作,然后编写数据。 在Haskell中,最好先编写一些代码,然后再编写代码来读取和写入数据,一旦你知道功能是正确的。
答案 2 :(得分:4)
你实际上不能在Haskell源文件中做同样的事情,所以我怀疑你实际上看起来像这样:
readFoo = do
x <- readFile "foo.txt"
let y = read x :: [Int]
return y
并且感到惊讶的是,readFoo
的类型以IO [Int]
形式出现,即使它返回的y
类型为[Int]
。
如果是这种情况,你的困惑的根源是Haskell中的return
不是命令式语言的返回语句。
return
是一个函数。一个非常普通的功能。与任何其他函数一样,它将某种类型的值作为输入,并为您提供其他类型的值作为输出。专门针对IO
的情况(return
可以与任何monad一起使用,但我们会在这里保持简单),它有这种类型:
a -> IO a
因此它需要任何类型的值,并为您提供IO
monad中包含的相同类型的值。因此,如果y
的类型为[Int]
,则return y
的类型为IO [Int]
,这就是readFoo
的结果。
无法获得[Int]
的{{1}}“out”。这是故意。 IO [Int]
的重点是任何依赖于程序“外部”的任何值都只能以IO
类型出现。因此,内部读取“foo.txt”并返回其中的整数列表的函数必须无法在Haskell中写入,否则整个卡片都会崩溃。如果我们看到一个类型为IO x
的函数,就像你试图编写的那样,那么我们知道readFoo :: [Int]
只能是一个特定的整数列表;它不能是依赖于磁盘上文件内容的列表。
答案 3 :(得分:2)
GHCi在命令提示符下更容易快速测试内容,这有点神奇。如果这是程序的一部分,那么IO [Int]
确实是正确的类型。 GHCi允许您将其视为Int
,以便为您节省一些打字。
这就是“为什么”。
现在,如果您有一个特定的问题,例如“如果类型签名是这样的话,我该如何做X?”......