我们:: a
的意思是“任何类型”,但不是子类型。a
可以是Int
, 或Bool
, 或IO (Maybe Ordering)
, 但没有特别的。a
不完全是类型,而是类型变量。
假设我们有一个这样的函数:
id x = x
编译器知道我们的参数没有特定的类型x
。我们可以使用任何类型 for x
,只要它与 id 中的任何内容等价即可。所以,我们这样写签名:
-- /- Any type in...
-- | /- ...same type out.
-- V V
id :: a -> a
请记住,在 Haskell 中,类型以大写字母开头。这不是一个类型:它是一个类型变量!
我们使用多态是因为这样做更容易。例如,组合是一个有用的想法:
(>>>) :: (a -> b) -> (b -> c) -> (a -> c)
(>>>) f g a = g (f a)
所以我们可以这样写:
plusOneTimesFive :: Int -> Int
plusOneTimesFive = (+1) >>> (* 5)
reverseHead :: [Bool] -> Bool
reverseHead = reverse >>> head
但是,如果我们必须像这样写出每种类型怎么办:
(>>>) :: (Bool -> Int) -> (Int -> String) -> (Bool -> String)
(>>>) f g a = g (f a)
(>>>') :: (Ordering -> Double) -> (Double -> IO ()) -> (Ordering -> IO ())
(>>>') f g a = g (f a)
(>>>'') :: (Int -> Int) -> (Int -> Bool) -> (Int -> Bool)
(>>>'') f g a = g (f a)
-- ...and so on.
那太傻了。
所以编译器使用类型统一来推断类型,如下所示:
假设我将其输入到 GHCi 中。6
在这里为了简单起见Int
。
id 6
编译器认为: " id :: a -> a
,它被传递一个Int
, so a = Int
, so id 6 :: Int
。
这不是子类型。可以使用类型类捕获子类型,但这是基本的多态性。