我想知道在GHCi中评估表达式时,Haskell使用的规则总是决定Integer实例而不是其他实例1 .+. 2:
import Debug.Trace
class MyFuns a where
(.+.) :: a ? a ? a
instance MyFuns Double where
x .+. y = trace "Double " $ x + y
instance MyFuns Integer where
x .+. y = trace "Integer " $ x + y
instance MyFuns Int where
x .+. y = trace "Int " $ x + y
Run Code Online (Sandbox Code Playgroud)
编辑:如果我在文件的末尾添加以下代码
main = do
let x = 1 .+. 2
print x
Run Code Online (Sandbox Code Playgroud)
为什么我会收到此错误?
No instance for (Num a0) arising from the literal ‘1’
The type variable ‘a0’ is ambiguous
Relevant bindings include x :: a0 (bound at fun2.hs:19:7)
Note: there are several potential instances:
instance Integral a => Num (GHC.Real.Ratio a)
-- Defined in ‘GHC.Real’
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Double -- Defined in ‘GHC.Float’
...plus three others
In the first argument of ‘(.+.)’, namely ‘1’
In the expression: 1 .+. 2
In an equation for ‘x’: x = 1 .+. 2
Run Code Online (Sandbox Code Playgroud)
然而,如果我在没有的GHCi提示符下加载文件main = ...,然后键入1 .+. 2,GHCi 3按预期打印.为什么会这样?
谢谢
请参阅Haskell报告中的4.3.4节.当Haskell读取一个看起来是整数的文字(没有小数部分)时,类型是(除非更具体地推断)Num a => a.在需要选择类型的那一刻,它使用默认规则,通常默认为Integer.当文字具有小数组件时,它Fractional a => a通常默认为Double.
通过使用default顶级声明,您可以更改这些设置,例如:
default (Int, Float)
Run Code Online (Sandbox Code Playgroud)
将默认Num到Int和Fractional到Float(因为Int没有Fractional).请注意,此语句的效果对于声明它的模块是本地的.
默认语句具有以下效果(引用报告):
每个默认变量都由默认列表中的第一个类型替换,该类型是所有模糊变量类的实例.如果没有找到这样的类型,则是静态错误.
-XExtendedDefaultRules GHC标志具有其他效果,请参见此处.
编辑
至于你的错误,来源是以下声明,该声明在GHC用户指南中,并在报告的第4.3.4节中有不同的措辞:
但是,用户必须指定类型是很烦人的,因此GHCi扩展了Haskell的类型默认规则(Haskell 2010报告的第4.3.4节),如下所示.标准规则为每个类型变量a采用每组约束(C1 a,C2 a,...,Cn a),并默认类型变量if
类型变量a不出现在其他约束中
所有Ci类都是标准的.
Ci类中的至少一个是数字.
我故意把重点放在第二个子弹上.因为您正在使用.+.,其中一个数字类是MyFuns- 它不是Prelude或标准库中的类,因此它不是"标准"类.幸运的是,案文继续如下:
在GHCi提示符下,或者如果给出-XExtendedDefaultRules标志,则使用GHC,以下附加差异适用:
因此放宽了上面的规则2:所有类Ci都是单参数类型类.
上面的规则3放宽了:Ci中的至少一个类是数字,或者是Show,Eq或Ord.
单元类型()被添加到标准类型列表的开头,这些类型在执行类型默认时尝试.
总之,如果你使用ExtendedDefaultRules标志(默认情况下你可以看到在GHCi中是活动的),你的代码也将使用你的自定义类编译得很好:
{-# LANGUAGE ExtendedDefaultRules #-}
import Debug.Trace
default (Int, Float, Double)
class MyFuns a where
(.+.) :: a -> a -> a
instance MyFuns Double where
x .+. y = trace "Double " $ x + y
instance MyFuns Integer where
x .+. y = trace "Integer " $ x + y
instance MyFuns Int where
x .+. y = trace "Int " $ x + y
main = do
print $ 1 .+. 2 -- Interpreted as Int
Run Code Online (Sandbox Code Playgroud)
请注意,在此示例中1.0 .+. 2.0被解释为double,而1.0 + 2.0被解释为float:这是因为没有MyFunsFloat实例,因此default跳过列表中的条目.