在GHCi中工作>但没有加载?

时间:2012-09-30 18:56:21

标签: haskell

我无法弄清楚为什么我会得到两个不同的结果,但我确定它与我开始讨厌的IO有关!

例如:

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

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

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

有人可以向我解释一下吗?因为我要抢购它!

感谢您的任何见解!

4 个答案:

答案 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数据类型。这很不方便,你写的代码很乱。

如何避免IO混乱

首先使用外部(文件或用户)数据完全解决没有的问题:

  • 将您通常从文件或用户读取的样本数据写为源代码中的值。
  • 使用定义中所需的任何数据将函数编写为函数的参数。 这是在编写纯代码时获取数据的唯一方法。首先编写纯代码。
  • 在样本数据上测试您的功能。 (如果你愿意,你可以在每次编写新函数时重新加载ghci,确保它符合你的期望。)
  • 一旦你的程序在没有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不是命令式语言的返回语句。

Haskell中的

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?”......