13

考虑以下 Haskell 函数:

sign a
  | a < 0 = (-1)
  | a > 0 = 1
  | otherwise = 0

当我将它加载到 ghci 中时,我希望:t sign是:

sign :: (Num a, Ord a) => a -> Integer

相反,它推断为:

*Main> :t sign
sign :: (Num a1, Num a, Ord a1) => a1 -> a

同样,如果我询问 integer 的类型5,我期望的Integer是,但我得到了

*Main> :t 5
5 :: Num a => a

我对 Haskell 的类型有些不理解。问题是,如果我所知道的返回类型sign只是它是类型类的一个实例Num,那么我应该无法将它的返回值传递给这个函数:

double :: Integer -> Integer
double x = x * 2

也就是说,我的double函数需要一个Integer,而不仅仅是Num.

然而,以下工作很好:

*Main> double (sign 5.5)
2

我对 Haskell 的类型系统有什么误解?

4

3 回答 3

19

问题是,如果我对“sign”的返回类型只知道它是类型类的一个实例Num,那么我应该无法将它的返回值传递给这个函数:

对,如果这就是你所知道的,你不能把它传递给double.

但是类型

sign :: (Num a1, Num a, Ord a1) => a1 -> a

意味着结果类型sign调用者要求的任何 Num类型。类型签名中的类型变量是(隐式)普遍量化的,而不是存在性的,例如 Java 接口。

sign可以产生任意类型的返回值,受限于它是 的实例Num,并且它返回的类型由调用上下文决定。

如果调用者想要一个Integer,它会得到一个。如果它想要一个Double,也没问题。

一开始忘了说:

同样,如果我询问整数 5 的类型,我希望是“整数”,但我得到了

    *Main> :t 5
    5 :: Num a => a

数字文字是多态的,整数文字代表fromInteger value,小数文字代表fromRational value

于 2013-03-16T10:58:26.600 回答
10

我只是想稍微澄清一下@DanielFischer 的回答。类似类型签名f :: Num b => a -> b意味着f能够返回 typeclass 的任何实例Num。当f被调用时,Haskell 使用上下文(调用者的类型签名)来确定b.

此外,Haskell 的数字文字就是这种多态性的一个例子。这就是为什么:t 5给你Num a => a。该符号5能够充当任何类型的数字,而不仅仅是整数。它出现的上下文决定了它将是什么。

于 2013-03-16T11:20:38.217 回答
7

在 Haskell 中,如果函数返回类型x是它的结果,这意味着调用者可以选择x应该是什么,而不是函数。相反,该函数必须能够返回任何可能的类型

sign可以返回任何类型的数据 - 包括Integer. 该double函数需要一个Integer,所以这很好 -sign可以返回它。

您可能不知道的另一部分难题:在 Java 中,2has typeint2.0has type double。但是在 Haskell 中,2有类型Num x => x——换句话说,任何可能的数字类型。(也2.0有 type Fractional x => x,这是一个类似的交易。)

于 2013-03-16T11:20:21.010 回答