Haskell规则用于在Int,Double和Integer的类型实例之间进行选择

mlj*_*jrg 4 haskell

我想知道在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按预期打印.为什么会这样?

谢谢

Sam*_*den 5

请参阅Haskell报告中的4.3.4节.当Haskell读取一个看起来是整数的文字(没有小数部分)时,类型是(除非更具体地推断)Num a => a.在需要选择类型的那一刻,它使用默认规则,通常默认为Integer.当文字具有小数组件时,它Fractional a => a通常默认为Double.

通过使用default顶级声明,您可以更改这些设置,例如:

default (Int, Float)
Run Code Online (Sandbox Code Playgroud)

将默认NumIntFractionalFloat(因为Int没有Fractional).请注意,此语句的效果对于声明它的模块是本地的.

默认语句具有以下效果(引用报告):

每个默认变量都由默认列表中的第一个类型替换,该类型是所有模糊变量类的实例.如果没有找到这样的类型,则是静态错误.

-XExtendedDefaultRules GHC标志具有其他效果,请参见此处.


编辑

至于你的错误,来源是以下声明,该声明在GHC用户指南中,并在报告的第4.3.4节中有不同的措辞:

但是,用户必须指定类型是很烦人的,因此GHCi扩展了Haskell的类型默认规则(Haskell 2010报告的第4.3.4节),如下所示.标准规则为每个类型变量a采用每组约束(C1 a,C2 a,...,Cn a),并默认类型变量if

  1. 类型变量a不出现在其他约束中

  2. 所有Ci类都是标准的.

  3. 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跳过列表中的条目.

  • @mljrg如果因为你加载了一些第三方模块而突然改变了默认类型,那可能会更加令人困惑... (2认同)