泛型是一种支持代码复用的语言特性。
例如,要计算数组的长度,数组里装的是什么类型并不重要。
正因如此,length函数在类型签名中使用了泛型类型参数a。
length : List a -> Int
当一个类型对另一个类型泛型时,我们为那个类型使用小写名称,并称之为类型参数。
常见的例子是List、Maybe和Result这些自定义类型。
-- Maybe is generic over the type a
type Maybe a
= Nothing
| Just a
someString : Maybe String
someString = Just "hello"
someInt : Maybe Int
someInt = Just 42
关于类型参数的名称,有一点很重要:它们可以由多个字符组成。 唯一的限制是,它们必须是小写名称。 因此,在有用的时候,我们倾向于给它们起描述性的名称。
一个类型可以对多个其他类型泛型,因而拥有多个类型参数。 这种情况下,每个独立的类型都会得到不同的小写名称。 相互独立并不妨碍它们成为同一个类型,只是让它们可以不同。
-- Result is generic over the types error and value
type Result error value
= Ok value
| Err error
记录类型别名也可以有类型参数,不过这种情况比较少见。
-- Event is generic over the type a
type alias Event a =
{ at : Time
, data : a
}
既然类型可以是泛型的,那么函数也可以。
可以编写处理泛型类型的函数,比如Maybe.withDefault。
withDefault : a -> Maybe a -> a
withDefault default maybe =
case maybe of
Just value -> value
Nothing -> default
把其他函数作为形参接收的函数称为高阶函数,它们也可以是泛型的。
map : (a -> b) -> Maybe a -> Maybe b
map function maybe =
case maybe of
Just value ->
Just (function value)
Nothing ->
Nothing
在这个练习中,你要编写一个通用的(也可以说是魔法般的!)TreasureChest,用来存放一些宝物。
定义一个只有一个变体的TreasureChest自定义类型。
这个变体应该有一个关联的String值,用来存放宝箱的密码。
这个变体还应该有一个关联的泛型值,用来存放宝物。这个值是泛型的,所以宝物可以是任何东西。
这个函数接收两个形参
String(用来尝试密码)TreasureChest泛型自定义类型这个函数应该把传入的密码尝试与TreasureChest中的String进行比对。
这个函数应该返回一个Maybe。
如果密码匹配,就返回Just,其中包含TreasureChest里的泛型值(也就是宝物!)。
如果密码不匹配,就返回Nothing。
这个(高阶)函数接收两个形参
TreasureChest泛型自定义类型,它的泛型类型也相同这个函数应该用宝箱里的宝物调用乘法函数。
这个函数应该返回一个新的 TreasureChest,密码与原宝箱相同,宝物则是相乘后的结果(也就是返回值)。