为什么在 Num 中没有定义(/)时,“1 / 2”会编译?

Why does "1 / 2" compile when there is no (/) defined in Num?

抱歉新手问题,但我真的不明白重载值:

GHCi, version 7.8.3: http://www.haskell.org/ghc/  :? for help
Prelude> let x = 1
Prelude> let y = 2

xy 都有类型 Num a => a。但是Num中没有定义(/)!那么为什么表达式 x / y 类型检查?

Prelude> x / y
0.5

(/)定义在classFractional中。是否有一些从 NumFractional 的隐式转换?

UPD:

所以,正如预期的那样,我得到了人们声称 x / y a 专门用于 Fractional a => a 的答案。

我创建了自己的数字层次结构:

data MyInt = MyInt Integer deriving Show
data MyDouble = MyDouble Double deriving Show

class MyNum a where
    (+#) :: a -> a -> a -- + renamed to +# to avoid collision with standard +
    myFromInteger :: MyNum a => Integer -> a

class MyNum a => MyFractional a where
    (/#) :: a -> a -> a

instance MyNum MyInt where
    (MyInt a) +# (MyInt b) = MyInt (a + b)
    myFromInteger i = MyInt i

instance MyNum MyDouble where
    (MyDouble a) +# (MyDouble b) = MyDouble (a + b)
    myFromInteger i = MyDouble (fromInteger i)

instance MyFractional MyDouble where
    (MyDouble a) /# (MyDouble b) = MyDouble (a / b)

如果 asnwers 中的所有内容都是正确的,则将 Num 替换为 MyNum 的类似代码也应该有效。但是ghci报错:

Prelude> :load myint.hs
*Main> let x = myFromInteger 1
*Main> let y = myFromInteger 2
*Main> x /# y

<interactive>:14:1:
    No instance for (MyFractional a0) arising from a use of `it'
    The type variable `a0' is ambiguous
    Note: there is a potential instance available:
      instance MyFractional MyDouble -- Defined at myint.hs:19:10
    In the first argument of `print', namely `it'
    In a stmt of an interactive GHCi command: print it

Haskell 会将分子转换为 Fractional 并执行分数除法。如果你需要划分 Int 尝试使用中缀运算符 div:

Prelude> x div y

看来您使用的是较新版本的 ghci,默认情况下启用了 NoMonomorphismRestriction。在这种情况下 xy 确实有类型 Num a => a:

Prelude> :set -XNoMonomorphismRestriction
Prelude> let x = 1
Prelude> let y = 2
Prelude> :t x
x :: Num a => a

并且这种类型,因为它是多态的,所以当你评估 x/y:

时,它是 specializedFractional a => a
Prelude> :t x/y
x/y :: Fractional a => a

因为 NumFractional 的超类,任何 Fractional 类型也是 Num 类型,因此特化了 xyFractional a => a 是可能的。这与做类似的事情没有什么不同:

($) = id :: (a -> b) -> (a -> b)

函数 $ 只是 id :: c -> c 的特化,其中 c = a -> b。在你的情况下,你正在专门化一个受约束的类型,并且约束可以专门用 sub类 替换父 类 (即更一般的具有更具体的约束)所以 Fractional a => a 是 [=17 的专门化=].

当 ghci 必须输出结果时,它必须选择 / 运算符的实现 来计算结果,因此它必须 instantiate 将类型转换为具体类型。这是由 defaulting 完成的,因此执行的操作是 Double 实例的操作,但它们可以使用 default 声明进行更改。


请注意,默认值仅适用于 内置 类。引用上面的link:

Ambiguities in the class Num are most common, so Haskell provides another way to resolve them---with a default declaration:

default (t1 , ... , tn)

where n>=0, and each ti must be a type for which Num ti holds. In situations where an ambiguous type is discovered, an ambiguous type variable, v, is defaultable if:

  • v appears only in constraints of the form C v, where C is a class, and at least one of these classes is a numeric class, (that is, Num or a subclass of Num), and
  • all of these classes are defined in the Prelude or a standard library (Figures 6.2--6.3, pages -- show the numeric classes, and Figure 6.1, page , shows the classes defined in the Prelude.)

如果 xy 的类型为 Num a => a,则表示它们是多态的。它并不意味着“某种类型,你所知道的只是它是 Num 的一个实例”,而是“任何类型,只要它是 Num 的一个实例”。所以 x / y 然后有'任何类型,只要它是 NumFractional 的实例,因为 Fractional 是 [=13 的子 class =] 是 '任何类型,只要它是 Fractional 的实例,像 RationalDouble 这样的类型满足。

顺便说一句,1 / 2 照字面意思也将进行类型检查,原因与(比如)1 :: Double2 :: Double 类型检查相同:1 in Haskell不限于整型,可用于任何数值类型。