Haskell来自整体 - 型混乱

Laz*_*535 3 haskell type-conversion

我正在经历"真实的世界Haskell",我正在做它随之而来的练习.

我注意到一些我觉得很奇怪的东西.

以此功能为例:

myAverage :: (Fractional a) => [a] -> Maybe a myAverage [] = Nothing myAverage xs = Just $ (mySum xs) / (fromIntegral $ myLength xs)

(/)函数需要两个作为实例的参数Fractional. myLength返回一个Int所以我使用the fromIntegral函数,因为我被告知.

fromIntegral只保证返回的值将是一个实例Num.

为什么在这种情况下Num也"包含"一个实例Fractional.不是Fractional更具体吗?

为什么编译器不抱怨我只有一个Num来自返回类型而不是"正确" Fractional.

为什么我不能Int直接使用它,它也是一个实例Num,不是吗?

在打电话的情况下,mySum我得到它.它需要一个Nums 的列表,但我用Fractionals 列表(它们也是Nums,因为它们是从它派生的)提供它.返回类型具有相同的类型,从类型注释(见下文)可以看出:单个元素Fractional

但是如果fromIntegral我无法推断自己(编译器显然可以:-))返回的值也是一个实例Fractional.为什么?它所采用的类型显然不是Fractional.

整个功能按预期工作.

只是为了澄清:

mySum :: (Num a) => [a] -> a myLength :: [a] -> Int

谢谢,

拉扎勒斯

sep*_*p2k 10

fromIntegral不产生一些我们只知道它是一个实例的未知类型Num.fromIntegral产生我们(通常含蓄地)要求它的任何类型,只要它的一个实例Num.

就OO语言而言,您需要将其视为泛型/模板,而不是虚拟继承/子类型多态.这是等价的Java签名fromIntegral会是什么样子<A extends Integral, B extends Num> B fromIntegral(A i),没有Num fromIntegral(Integral i).

所以,如果我们想要一个Int,我们得到一个Int.如果我们想要一个Integer,我们得到一个Integer.如果我们想要一个Double,我们得到一个Double.但是,如果我们想要一个String,我们运气不好,因为String它不是一个实例Num.

在这种情况下,我们希望awhere a是给定列表的元素类型.因此,如果我们传入一个Doubles 列表,fromIntegral给我们一个Double,如果我们传入一个Rationals 列表,fromIntegral给我们一个Rational.


Eri*_*ikR 6

你问:

但是,从整体来说只保证返回的值将是Num的一个实例.

这是真的,但请考虑以下类型fromIntegral:

fromIntegral :: (Integral a, Num b) => a -> b
Run Code Online (Sandbox Code Playgroud)

因此,fromIntegral允许我们将其转换Int任何其他Num实例.

暂时,让我们处理具体类型而不是类型类.假设您已经定义myAverage如下:

myAverage :: [Double] -> Maybe Double
myAverage [] = Nothing
myAverage xs = Just $ mySum xs / (fromIntegral (myLength xs))
Run Code Online (Sandbox Code Playgroud)

Double支持除法,并fromIntegral能够将Int返回的转换myLength为a Double.

如果你使用了相同的定义将工作Rational代替Double,例如:

myAverage :: [Rational] -> Maybe Rational
Run Code Online (Sandbox Code Playgroud)

返回类型类及其类型检查方式:

myAverage :: Fractional a => [a] -> Maybe a
...
myAverage xs = Just $ mySum xs / (fromIntegral (myLength xs))
Run Code Online (Sandbox Code Playgroud)

这是怎么回事:

  1. a是Fractional,而Fractional是Num的子类,所以a也是Num.
  2. xs有类型[a],所以mySum xs有类型a
  3. myLength xs 有类型 Int
  4. fromIntegral可以转换Int为任何其他Num实例,因此它可以转换Inta
  5. mySum xs而且fromIntegral表达式都有a一个类型,这是一个Fractional,所以我们可以执行除法.