我是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"
有没有人有任何想法可能导致这种情况以及如何解决这个问题?
答案 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
的值。因此,对于Int
,Read 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
适用于Int
和Double
!
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没有信息可以找出您想要的类型。您想要Int
,Bool
,()
,String
,......
你需要告诉它,
read "456" :: Int
让它知道。
在实际程序中,通常会有确定所需类型的上下文,然后可以推断出它并且您不需要手动提供类型。在提示符下,没有任何上下文可以帮助推断。