为什么我被迫在这里指定一个类型?

Gre*_*aro 4 haskell typeclass

我有以下代码:

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
Run Code Online (Sandbox Code Playgroud)

我能做到toString "Text"并且toString 't'都编译得很好.但如果我这样做,toString 5我得到错误.我被迫做了toString (5::Int).

show不需要指定的类型工作.当我看到实现时Show,我没有看到任何神奇的东西:

instance Show Int where ...

instance Show Integer where ...

我做错了什么需要我指定类型以及如何修复它?

更新:

{-# LANGUAGE ExtendedDefaultRules #-}按照下面的建议添加,它完美地工作.解决了我的问题.

Rei*_*chs 7

您需要指定一个类型,因为5在Haskell中是多态的:

?> :type 5
5 :: Num a => a
Run Code Online (Sandbox Code Playgroud)

所以编译器不知道Num选择哪个实例.然而,由于扩展违约,这确实在ghci的工作:

?> toString 5
"5"
Run Code Online (Sandbox Code Playgroud)

  • 具体来说,`5`表示`fromInteger(5 :: Integer)`,所以`toString 5`是不明确的,原因与`show(read 5)`是 - 未指定中间类型,因此编译器没有'知道在没有更多信息的情况下使用哪个实例,例如类型签名. (5认同)
  • 为什么同样的论点不适用于`show`? (2认同)

Ben*_*Ben 6

当你写toString "Text"或者toString 't',Haskell是能够准确的告诉哪些具体类型"Text"'t'主要有:[Char](又名String)和Char分别.所以它可以选择一个实例并运行你的代码.

toString 5略有不同.类似的数字文字5在Haskell中被重载,因此它5可以是a Int,a Double,或者与没有Num实例的实例完全不同的东西ToString.因此,这不是指定其中的一个实例ToString,我们应该使用(请注意,你会得到基于您是否选择现有的情况下,不同的观察到的行为IntDouble).Haskell对这种情况的一般反应是报告一个模糊的类型错误,要求你做一些事情来更多地确定类型.

Haskell规范的作者认为这是一个非常普遍且烦人的问题,尤其是数字类型,因此它们构建了一种默认类型的默认机制,在某些条件下设计相当保守,以帮助使用标准数字文字Haskell仅限功能.1

只有两者都是默认类型:

  1. 它的所有约束只涉及内置类型类
  2. 至少其中的一个类是数字(Num,Floating等)

因此,show 5因为对类型的约束5是有效的(Num a, Show a); Num是一个数字类,所有约束都是内置类.

toString 5看起来它应该基本相同,但你得到了约束(Num a, ToString a),并且因为ToString不是内置类默认不适用,留下一个模糊的类型错误.

GHCi中使用的扩展违约规则(或者如果使用ExtendedDefaultRules扩展名)放宽了"仅内置类"规则(以及其他扩展),这样就完全(Num a, ToString a)符合违约条件.

GHC用户手册讨论了如何在GHCi(或ExtendedDefaultRule扩展名为s)中扩展通常的类型默认规则,并将它们与标准规则进行比较:

https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html#type-defaulting-in-ghci


1在实践中,我发现它在实际开发中是一个非常罕见的问题(我通常使用一个警告类型默认的标志进行编译),但这是因为我采用了(通常)做法,几乎为所有顶级函数提供类型签名.当调用GHC来编译使用该实践编写的整个模块时,几乎总是有足够的信息可用于指定所有类型,并且永远不需要默认.

例如,toString x如果它出现在一个x带有类型的参数的函数中,那就完全没问题了Int.如果x是没有显式类型的局部变量,它甚至会很好,但它也被传递给需要Int参数的函数.或者被列入一个列表以及其他已知的东西Int.等等

但是在GHCi中,您提供的表达式可以一次一个地进行分析,并且通常不会打扰类型签名等可选项.在这些条件下的数字文字(和其他表达式)被模糊输入比较频繁,这对GHCI的扩展默认规则的动机.