#let f = function `On -> 1 | `Off -> 0 | `Number n -> n;;
val f : [< `Number of int | `Off | `On ] -> int = <fun>
# List.map f [`On; `Off];;
- : int list = [1; 0]
[>`Off|`On] list 表示要匹配此列表,您至少應能夠匹配不帶引數的 `Off 和 `On。[<`On|`Off|`Number of int] 表示 f 可以應用於不帶引數的 `Off、`On,或 `Numbern,其中 n 為整數。變體類型內的 > 和 < 表示它們仍可通過定義更多標籤或允許更少標籤來改進。因此,它們包含隱式的類型變數。由於每個變體類型在整個類型中僅出現一次,因此不會顯示其隱式的類型變數。
val f : ([> `A | `B | `C | `D ] as 'a) -> 'a = <fun>
# f `E;;
- : [> `A | `B | `C | `D | `E ] = `E
這裡我們看到了兩種現象。首先,由於此匹配是開放的(最後一個 case 會捕獲任何標籤),我們得到 [> `A | `B] 類型,而不是封閉匹配中的 [< `A | `B] 類型。然後,由於 x 是原樣返回,因此輸入和返回類型相同。as 'a 表示此類型的共享。如果我們將 f 應用於另一個標籤 `E,它將被添加到列表中。
#let f1 = function `A x -> x = 1 | `B -> true | `C -> falselet f2 = function `A x -> x = "a" | `B -> true ;;
val f1 : [< `A of int | `B | `C ] -> bool = <fun> val f2 : [< `A of string | `B ] -> bool = <fun>
#let f x = f1 x && f2 x;;
val f : [< `A of string & int | `B ] -> bool = <fun>
這裡 f1 和 f2 都接受變體標籤 `A 和 `B,但 `A 的引數對於 f1 是 int,對於 f2 是 string。在 f 的類型中,只有 f1 接受的 `C 消失了,但是 `A 的兩個引數類型都顯示為 int & string。這表示如果我們將變體標籤 `A 傳遞給 f,其引數應該 同時 是 int 和 string。由於沒有這樣的值,因此 f 無法應用於 `A,並且 `B 是唯一接受的輸入。
#let num x = `Num x let eval1 eval (`Num x) = x letrec eval x = eval1 eval x ;;
val num : 'a -> [> `Num of 'a ] = <fun> val eval1 : 'a -> [< `Num of 'b ] -> 'b = <fun> val eval : [< `Num of 'a ] -> 'a = <fun>
#let plus x y = `Plus(x,y) let eval2 eval = function | `Plus(x,y) -> eval x + eval y | `Num _ as x -> eval1 eval x letrec eval x = eval2 eval x ;;
val plus : 'a -> 'b -> [> `Plus of 'a * 'b ] = <fun> val eval2 : ('a -> int) -> [< `Num of int | `Plus of 'a * 'a ] -> int = <fun> val eval : ([< `Num of int | `Plus of 'a * 'a ] as 'a) -> int = <fun>
為了讓使用上更方便,你可以使用型別定義作為「或」模式的縮寫。也就是說,如果你定義了 type myvariant = [`Tag1 of int | `Tag2 of bool],那麼模式 #myvariant 就等同於寫成 (`Tag1(_ : int) | `Tag2(_ : bool))。
這種縮寫可以單獨使用,
#let f = function | #myvariant -> "myvariant" | `Tag3 -> "Tag3";;
val f : [< `Tag1 of int | `Tag2 of bool | `Tag3 ] -> string = <fun>
#let f = function | `As -> "A" | #abc -> "other" ;;
val f : [< `A | `As | `B | `C ] -> string = <fun>
#let f : abc -> string = f ;;
val f : abc -> string = <fun>
你可以透過註解定義本身來避免此類風險。
#let f : abc -> string = function | `As -> "A" | #abc -> "other" ;;
Error: This pattern matches values of type [? `As ] but a pattern was expected which matches values of type abc The second variant type does not allow tag(s) `As