val store : '_weak1 option ref = {contents = None}
由於 None 的類型為 'a option,且函式 ref 的類型為 'b -> 'b ref,因此 store 的自然推斷類型為 'a option ref。但是,推斷出的類型 '_weak1 option ref 卻不同。名稱以 _weak 作為前綴的類型變數(如 '_weak1)是弱多型類型變數,有時縮寫為「弱類型變數」。弱類型變數是目前未知之單一類型的佔位符。一旦佔位符類型 '_weak1 背後的特定類型 t 已知,所有出現的 '_weak1 都將被替換為 t。例如,我們可以定義另一個選項參考,並在其中儲存一個 int
#let another_store = ref None ;;
val another_store : '_weak2 option ref = {contents = None}
# another_store := Some 0; another_store ;;
- : int option ref = {contents = Some 0}
在 another_store 內部儲存 int 之後,another_store 的類型已從 '_weak2 option ref 更新為 int option ref。弱多型類型變數和泛型多型類型變數之間的這種區別,可以保護 OCaml 程式免於不健全性和執行階段錯誤。為了理解不健全性可能來自何處,請考慮這個簡單的函式,如果存在值,它會將值 x 與儲存在 store 參考中的值交換
#let swap store x = match !store with | None -> store := Some x; x | Some y -> store := Some x; y;;
val swap : 'a option ref -> 'a -> 'a = <fun>
我們可以將此函式套用至我們的 store
#let one = swap store 1 let one_again = swap store 2 let two = swap store 3;;
val one : int = 1 val one_again : int = 1 val two : int = 2
#type 'a regular_nested = List of 'a list | Nested of 'a regular_nested list let l = Nested[ List [1]; Nested [List[2;3]]; Nested[Nested[]] ];;
type 'a regular_nested = List of 'a list | Nested of 'a regular_nested list val l : int regular_nested = Nested [List [1]; Nested [List [2; 3]]; Nested [Nested []]]
請注意,類型建構子 regular_nested 在上面的定義中始終以 'a regular_nested 的形式出現,並具有相同的參數 'a。有了這個類型,可以使用經典的遞迴函數計算最大深度
#letrec maximal_depth = function | List _ -> 1 | Nested [] -> 0 | Nested (a::q) -> 1 + max (maximal_depth a) (maximal_depth (Nested q));;
val maximal_depth : 'a regular_nested -> int = <fun>
#type 'a nested = List of 'a list | Nested of 'a list nested;;
type 'a nested = List of 'a list | Nested of 'a list nested
直觀上,類型為 'a nested 的值是一個列表的列表…包含 k 個嵌套列表的元素 a 的列表。然後,我們可以將在 regular_depth 上定義的 maximal_depth 函數改編成計算這個 k 的 depth 函數。首先,我們可以嘗試定義
#letrec depth = function | List _ -> 1 | Nested n -> 1 + depth n;;
Error: This expression has type 'a list nested but an expression was expected of type 'a nested The type variable 'a occurs inside 'a list
這裡的類型錯誤來自於,在定義 depth 期間,類型檢查器首先將類型 'a -> 'b 分配給 depth。在鍵入模式匹配時,'a -> 'b 變成 'a nested -> 'b,然後在鍵入 List 分支後變成 'a nested -> int。但是,當鍵入 Nested 分支中的應用程式 depth n 時,類型檢查器會遇到問題:depth n 應用於 'a list nested,因此它必須具有類型 'a list nested -> 'b。將此約束與先前的約束統一起來會導致不可能的約束 'a list nested = 'a nested。換句話說,在其定義中,由於類型建構子 nested 的非正規性,遞迴函數 depth 應用於具有不同類型 'a 的類型 'a t 的值。這會造成問題,因為類型檢查器僅在函數 depth 的 *定義* 處引入了一個新的類型變數 'a,而在此處,我們需要為函數 depth 的每個 *應用程式* 使用不同的類型變數。
#letrec depth: 'a. 'a nested -> int = function | List _ -> 1 | Nested n -> 1 + depth n;;
val depth : 'a nested -> int = <fun>
# depth ( Nested(List [ [7]; [8] ]) );;
- : int = 2
在 depth 的類型 'a.'a nested -> int 中,類型變數 'a 是全稱量化的。換句話說,'a.'a nested -> int 的意思是「對於所有類型 'a,depth 將 'a nested 的值映射到整數」。而標準類型 'a nested -> int 可以解釋為「讓 'a 成為一個類型變數,然後 depth 將 'a nested 的值映射到整數」。這兩個類型表達式有兩個主要區別。首先,顯式多型註解告訴類型檢查器,每次應用函數 depth 時,它都需要引入一個新的類型變數。這解決了我們在定義函數 depth 時遇到的問題。
#let len nested = let map_and_sum f = List.fold_left (fun acc x -> acc + f x) 0 inletrec len: 'a. ('a list -> int ) -> 'a nested -> int = fun nested_len n -> match n with | List l -> nested_len l | Nested n -> len (map_and_sum nested_len) n in len List.length nested;;
#let shape n = letrec shape: 'a 'b. ('a nested -> int nested) -> ('b list list -> 'a list) -> 'b nested -> int nested = fun nest nested_shape -> function | List l -> raise (Invalid_argument "shape requires nested_list of depth greater than 1") | Nested (List l) -> nest @@ List (nested_shape l) | Nested n -> let nested_shape = List.map nested_shape inlet nest x = nest (Nested x) in shape nest nested_shape n in shape (fun n -> n ) (fun l -> List.map List.length l ) n;;