第2 章中的許多歸納函數定義都循著同一個固定模式。
以 sum, length, 與 map f 為例:
sum :: List Int -> Int
sum [] = {-"{\color{burntorange}"-} 0{-"}"-}
sum (x:xs) = {-"{\color{burntorange}"-} x +{-"}\,"-} sum xs
length :: List a -> Nat
length [] = {-"{\color{burntorange}"-} Zero{-"}"-}
length (x:xs) = {-"{\color{burntorange}"-} Suc{-"}"-} (length xs)
map :: (a -> b) -> List a -> List b
map f [] = {-"{\color{burntorange}"-} []{-"}"-}
map f (x:xs) = {-"{\color{burntorange}"-} f x :{-"\,}"-} map f xs
它們都在輸入為 [] 時傳回某個基底值,在輸入為 x:xs 時在 xs 上遞迴呼叫,
並將呼叫結果稍作加工。
三者的不同之處只在橘色的部分,即基底值以及用於加工的函數:sum 使用 0 與 (+), length 使用 Zero 與 Suc, map f 則使用 [] 與 (f x :).
如果說「抽象化」是一個高階程式語言給我們的最重要能力,我們能否將這個模式抽象出來呢?
我們把上述三個定義中橘色的部分抽出變成參數,將餘下的函數稱為 foldr:
foldr :: (a -> b -> b) -> b -> List a -> b
foldr f e [] = e
foldr f e (x:xs) = f x (foldr f e xs)
如此一來,sum, length, 與 map f 都是 foldr 的特例:
sum = foldr (+) 0
length = foldr (\x n -> Suc n) Zero
map f = foldr (\x ys -> f x : ys) []
函數 foldr 是串列上的「摺(fold)」 --- foldr 一詞是 fold 與「右邊(right)」的縮寫,意謂該函數是一個往右結合的摺。我們將在下一節解釋。