我有以下代码:
class ToString a where
toString :: a -> String
instance ToString String where
toString a = a
instance ToString Char where
toString a = [a]
instance ToString Int where
toString a = show a
instance ToString Integer where
toString a = show a
instance ToString Float where
toString a = show a
instance ToString Double where
toString a = show a
我可以toString "Text"
和toString 't'
进行编译。但如果我toString 5
我得到并且错误。我被迫做toString (5::Int)
。
show
不需要指定的类型。当我看到Show
的实现时,我看不到任何神奇的东西:
instance Show Int where ...
instance Show Integer where ...
我做错了什么,需要我指定类型以及如何解决?
更新
我按照以下建议添加了{-# LANGUAGE ExtendedDefaultRules #-}
,并且效果很好。解决了我的问题。
答案 0 :(得分:7)
您需要指定一个类型,因为5
在Haskell中是多态的:
λ> :type 5
5 :: Num a => a
因此编译器不知道选择哪个Num
实例。但是,由于extended defaulting,此 在ghci中工作:
λ> toString 5
"5"
答案 1 :(得分:6)
当您编写toString "Text"
或toString 't'
时,Haskell能够确切地确定具体类型"Text"
和't'
:[Char]
(又名{{1}分别是}和String
。因此,它可以选择一个实例并运行您的代码。
Char
略有不同。像toString 5
这样的数字文字在Haskell中被重载,因此5
可以是5
,Int
,或与Double
实例完全不同的东西没有Num
个实例。因此,我们没有指定我们应该使用哪个ToString
实例(请注意,根据您选择ToString
还是Int
,您将获得与现有实例不同的可观察行为。 Haskell对这种情况的一般反应是报告一个模糊的类型错误,要求你做一些事情来确定更多的类型。
Haskell规范的作者认为这对于特别是数字类型来说是一个非常常见且烦人的问题,所以他们在一定的条件下建立了默认模糊类型的机制保守地设计用于帮助仅使用标准Haskell功能的数字文字。 1
只有两种情况都会默认类型:
Double
,Num
等)所以Floating
有效,因为show 5
类型的约束是5
; (Num a, Show a)
是一个数字类,所有约束都是内置类。
Num
似乎应该基本相同,但是你得到约束toString 5
,并且由于(Num a, ToString a)
不是内置类,默认不适用,离开你有一个模糊的类型错误。
GHCi中使用的扩展默认规则(或者如果使用ToString
扩展名)放宽了“仅内置类”规则(以及其他扩展名),这使得ExtendedDefaultRules
符合默认条件后的默认值所有
GHC用户手册讨论了如何在GHCi(或(Num a, ToString a)
的扩展名)中扩展通常的类型默认规则,并将它们与标准规则进行比较:
https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html#type-defaulting-in-ghci
1 在实践中,我发现它在实际开发中是一个非常罕见的问题(我通常用一个警告类型默认的标志进行编译),但这是因为我采用了(常见的)实践为几乎所有顶级函数提供类型签名。当调用GHC来编译使用该实践编写的整个模块时,几乎总是有足够的信息可用于指定所有类型,并且永远不需要默认。
例如,如果ExtendedDefaultRule
发生在toString x
是类型为x
的参数的函数中,则Int
完全没问题。如果x
是没有显式类型的局部变量,那么它甚至会很好,但它也被传递给需要Int
参数的函数。或者被列入列表以及其他已知为Int
的内容。等等。
但是在GHCi中,您提供的表达式可以一次一个地进行分析,并且通常不会打扰类型签名等可选项。在这些条件下,数字文字(和其他表达式)被更加模糊地键入更多,这是GHCi扩展默认规则的动机。