Haskell Read(无实例)

时间:2013-07-17 21:03:32

标签: haskell instance overloading ghci

我是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"

有没有人有任何想法可能导致这种情况以及如何解决这个问题?

2 个答案:

答案 0 :(得分:12)

首先,要解决您的问题,您可以通过以下几种方式之一指定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类型类是为许多类型定义的,它说错误中定义了28个实例。它是为Int,Integer,Double和String以及无数其他人定义的。因此,为了让编译器知道要使用的类型类的实例,它需要能够从您指定的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
亲爱的,亲爱的。因此read适用于IntDouble

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

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

readInt :: String -> Int
readInt = read

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

readInt "456"

编译器会知道readInt的类型为String -> Int,因此readInt "456"的类型为Int。毫不含糊,就像Haskell喜欢它一样。

附录

GHCi中的语法对于定义事物有点不同,所以如果你专门使用GHCi来玩Haskell,我建议你切换到加载.hs文件并使用:r命令重新加载他们。 GHCi的一个问题是你必须用“let”来定义所有内容,当你开始编写程序时,它们会开始变得奇怪,其中顶级定义不需要它。另一个问题是monomorphism restriction,坦率地说,这只是一种奇怪和古怪,而且这里不值得详细介绍。

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

let readInt = read :: String -> Int

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

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

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

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

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

答案 1 :(得分:9)

键入

read "456"

在提示符处,ghci没有信息可以找出您想要的类型。您想要IntBool()String,......

你需要告诉它,

read "456" :: Int

让它知道。

在实际程序中,通常会有确定所需类型的上下文,然后可以推断出它并且您不需要手动提供类型。在提示符下,没有任何上下文可以帮助推断。