Haskell Read (no instance)



我是Haskell初学者,有一个奇怪的问题。到目前为止,一切都很顺利,我已经能够正常使用Prelude阅读功能。现在突然间,我必须不断声明它的类型才能使用它。

我总是必须声明这个或类似的东西才能使用它。

let r = read::String-> Int
我尝试

重新启动ghci,认为我不小心使读取过载,但是每当我尝试正常使用它时

read "456"

我收到以下错误

No instance for (Read a0) arising from a use of `read'
The type variable `a0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
  instance Read () -- Defined in `GHC.Read'
  instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
  instance (Read a, Read b, Read c) => Read (a, b, c)
    -- Defined in `GHC.Read'
  ...plus 25 others
In the expression: read "456"
In an equation for `it': it = read "456"

有没有人知道可能导致这种情况的原因以及如何解决它?

首先,要解决您的问题,您可以通过以下几种方式之一指定您期望从read中获得的结果:

首先,通过指定整个计算结果的类型:

read "456" :: Int

或者通过指定函数的类型读取:

(read :: String -> Int) "456"

为了解释为什么会发生这种情况,问题是read的结果类型是多态的。也就是说,定义了许多不同的read函数,并且它们被定义为类型类Read的一部分。该错误为您提供了原因:

Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
  instance Read () -- Defined in `GHC.Read'
  instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
  instance (Read a, Read b, Read c) => Read (a, b, c)
    -- Defined in `GHC.Read'
  ...plus 25 others

Read typeclass 是为许多类型定义的,它表示错误中定义了 28 个实例。它是为 Int、整数、双精度、字符串以及无数其他定义的。因此,为了让编译器知道要使用的 typeclass 实例,它需要能够从您指定的read类型推断出这一点。如果你给它:: String -> Int,编译器可以弄清楚。

要理解它是如何工作的,让我们看一下 Read 的类型类的一部分:

class Read a where
  read :: String -> a

所以对于给定的类型a,必须定义Read a的实例,以便函数read返回类型a的值。所以对于IntRead Int的实例有一个定义,在GHC-Read中确实有。该实例定义了read的工作方式。不幸的是,该实例相当迟钝,依赖于其他函数和其他类似的东西,但为了完整起见,这里是:

instance Read Int where
  readPrec     = readNumber convertInt
  readListPrec = readListPrecDefault
  readList     = readListDefault

但是,要意识到的重要一点是,此实例意味着read将适用于Int。但不久之后,出现了这样一个例子:

instance Read Double where
  readPrec     = readNumber convertFrac
  readListPrec = readListPrecDefault
  readList     = readListDefault

哦,亲爱的。所以readIntDouble都有效!

Haskell语言确保编译器通常会尝试推断值的唯一类型或失败。有一些奇怪的例外,对GHC中的语言进行了一些扩展,但你必须记住,Haskell试图阻止你在脚上开枪。如果您没有以某种方式指定您期望从 read 的类型,那么编译器可以推断任何类型,但这可能无效。您不希望您的代码突然开始以浮点 Double s 或任意精度 Integer s 读取,而您以前期望在Int中读取,对吗?

在这种情况下,不指定类型只会导致编译器放弃,它无法明确回答您向它提出的问题,即"我使用哪个Read实例?Haskell编译器不喜欢猜测。防止它猜测的一种方法是以后以明确的方式使用值,或者定义一个明确使用read的函数。例如,可以在代码中使用此函数,以避免键入:: String -> Int

readInt :: String -> Int
readInt = read

编译器可以确定它需要在那里使用哪个Read实例,如果你这样做,可以在你的代码中使用:

readInt "456"

编译器将知道readInt具有类型String -> Int,因此readInt "456"具有类型Int。毫不含糊,就像哈斯克尔喜欢的那样。

补遗

GHCi 中的语法在定义事物方面略有不同,因此如果您专门使用 GHCi 来玩 Haskell,我建议您切换到加载 .hs 文件并使用 :r 命令重新加载它们。GHCi的一个问题是你必须用"let"来定义所有内容,当你开始编写程序时,这会开始感到奇怪,而顶级定义不需要它。另一个问题是单态限制,坦率地说,它有点奇怪和古怪,不值得在这里详细介绍它。

无论如何,在GHCi中定义上述readInt的语法很简单,尽管有两种方法(等效(。你已经知道一个,就是这样做:

let readInt = read :: String -> Int

另一种方法是分别定义函数及其类型,这更类似于在常规 .hs 文件中完成它的方式:

let readInt :: String -> Int; readInt = read

两者都不是惯用的Haskell,但那是因为GHCi对编写代码施加了古怪的限制,并且多行输入很奇怪。你可以这样做:

:{
let readInt :: String -> Int;
    readInt = read
:}

这将使您接近惯用的Haskell定义。但是,如果您将其放在 .hs 文件中并使用 :load 或指定带有 ghci ./somefile.hs 的文件,GHCi 将正确编译我的初始示例。

当您键入时

read "456"

在提示符下,GHCI 没有信息来找出您想要的类型。你想要一个Int,一个Bool()String,...

你需要告诉它,

read "456" :: Int

以便它知道。

在实际程序中,通常有上下文来确定所需的类型,然后可以推断出来,而不需要手动提供类型。在提示符下,没有上下文可以帮助推理。

相关内容

最新更新