考虑以下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
同样,如果我要求整数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的类型系统的误解是什么?
答案 0 :(得分:19)
问题是,如果我所知道的'sign'的返回类型是它是
Num
类型类的实例,那么我就不能将它的返回值传递给这个函数:< / p>
是的,如果这就是你所知道的,你就无法将其传递给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
是小数文字。
答案 1 :(得分:10)
我只是想澄清@ DanielFischer的答案。像f :: Num b => a -> b
这样的类型签名意味着f
能够返回类型Num
的任何实例。调用f
时,Haskell使用上下文(调用者的类型签名)来确定b
的具体类型。
此外,Haskell的数字文字就是这种多态的一个例子。这就是:t 5
给你Num a => a
的原因。符号5
能够充当任何类型的数字,而不仅仅是整数。它出现的上下文决定了它将会是什么。
答案 2 :(得分:6)
在Haskell中,如果函数返回类型x
是其结果,这意味着调用者可以选择x
应该是什么,而不是函数< / em>的。相反,函数必须能够返回任何可能的类型。
您的sign
可以返回任何类型的数据 - 包括Integer
。 double
函数想要一个Integer
,所以没关系 - sign
可以返回。{/ p>
您可能不知道的另一部分难题:在Java中,2
的类型为int
,2.0
的类型为double
。但在Haskell中,2
的类型为Num x => x
- 换句话说,任何可能的数字类型。 (同样2.0
的类型为Fractional x => x
,这是类似的交易。)