泛型是一種語言特性,能讓程式碼重複使用。
舉例來說,要計算一個陣列的長度,並不應該取決於它裝的是什麼型別。
這就是為什麼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,密碼與原本的相同,並帶有倍乘後的寶藏(也就是回傳值)