Haskell来自整体 - 型混乱

时间:2015-08-06 12:33:50

标签: 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的情况下,我得到它。它需要一个Num的列表,但我用Fractional的列表(也是Num s)来提供它,因为它们是从它派生的。返回类型具有相同的类型,从类型注释(见下文)可以看出:Fractional

的单个元素

但是在fromIntegral的情况下,我无法推断自己(编译器显然可以:-))返回的值也是Fractional的实例。为什么? 它所采用的类型显然不是Fractional

整个功能按预期工作。

只是为了澄清:

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

谢谢,

拉​​扎勒斯

2 个答案:

答案 0 :(得分:10)

fromIntegral不会产生一些未知类型,我们只知道它是Num的一个实例。 fromIntegral生成我们(通常隐式)要求的任何类型,只要它是Num的实例。

就OO语言而言,您需要将其视为泛型/模板,而不是虚拟继承/子类型多态。这就是fromIntegral的等效Java签名看起来像<A extends Integral, B extends Num> B fromIntegral(A i),而不是Num fromIntegral(Integral i)

因此,如果我们想要Int,我们会得到Int。如果我们想要Integer,我们会得到Integer。如果我们想要Double,我们会得到Double。但是,如果我们想要String,我们就不幸运了,因为String不是Num的实例。

在这种情况下,我们需要a,其中a是给定列表的元素类型。因此,如果我们传入Double s列表,fromIntegral会向我们提供Double,如果我们传入Rational s列表,fromIntegral就会给我们Rational

答案 1 :(得分:6)

你问:

  

但是,只有整合才保证返回的值将是Num的实例。

这是真的,但要考虑fromIntegral的类型:

fromIntegral :: (Integral a, Num b) => a -> b

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

目前,让我们处理具体类型而不是类型类。假设你已经像这样定义了myAverage

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

Double支持分组,fromIntegral能够将从Int返回的myLength转换为Double

如果您使用Rational代替Double,则相同的定义也适用,例如:

myAverage :: [Rational] -> Maybe Rational

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

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

这是怎么回事:

  1. a是Fractional,而Fractional是Num的子类,因此a也是Num。
  2. xs的类型为[a],因此mySum xs的类型为a
  3. myLength xs的类型为Int
  4. fromIntegral可以将Int转换为任何其他Num实例,因此可以将Int转换为a
  5. mySum xs和fromIntegral表达式的类型a都是小数,因此我们可以执行除法。