类型变量

类型变量是类型签名中的非大写字母名称。与他们的大写对手不同,例如 IntString,它们不代表任何类型,而是任何类型。它们用于编写可以在任何类型或类型上运行的泛型函数,对于通过 ListDict 等容器编写操作特别有用。例如,List.reverse 函数具有以下签名:

reverse : List a -> List a

这意味着它可以在任何类型值的列表上工作,所以 List IntList (List String),这些和其他任何一个都可以完全相同。因此,a 是一个可以代表任何类型的类型变量。

reverse 函数可以在其类型签名中使用任何非大写的变量名,除了少数特殊类型的变量名,例如 number(有关更多信息,请参阅相应的示例):

reverse : List lol -> List lol

reverse : List wakaFlaka -> List wakaFlaka

只有当单个签名中有不同的类型变量时,类型变量的名称才有意义,例如列表中的 map 函数:

map : (a -> b) -> List a -> List b

map 从任何类型的 a 到任何类型的 b,以及包含某些类型 a 的元素的列表,并返回一些类型 b 的元素列表,它通过将给定的函数应用于列表的每个元素来获取。

让我们使签名具体化,以便更好地看到这一点:

plusOne : Int -> Int
plusOne x = 
    x + 1

> List.map plusOne
<function> : List Int -> List Int

我们可以看到,在这种情况下,a = Intb = Int。但是,如果 map 有一个像 map : (a -> a) -> List a -> List a 这样的类型签名,那么它只适用于在单一类型上运行的函数,并且你永远无法通过使用 map 函数来改变列表的类型。但由于 map 的类型签名有多个不同的类型变量 ab,我们可以使用 map 来改变列表的类型:

isOdd : Int -> Bool
isOdd x =
    x % 2 /= 0

> List.map isOdd
<function> : List Int -> List Bool

在这种情况下,a = Intb = Bool。因此,为了能够使用可以获取和返回不同类型的函数,必须使用不同的类型变量。