letrec eval : type a. a term -> a = function | Int n -> n (* a = int *) | Add -> (fun x y -> x+y) (* a = int -> int -> int *) | App(f,x) -> (eval f) (eval x) (* eval 在型態 (b->a) 和 b 上呼叫,以獲得新的 b *)
GADT 的型態推論眾所周知地困難。這是因為某些型態在超出分支時可能會變得模糊不清。例如,在上面的 Int 情況下,n 的型態可以是 int 或 a,並且它們在該分支之外並不等效。作為第一種近似,如果模式匹配標註的型態不包含自由型態變數(檢視值和回傳型態),則型態推論始終有效。由於包含的型態註釋僅包含局部抽象型態,因此上面的範例就是這種情況。
我們在上面定義的 term 型態是一種索引型態,其中型態參數反映了數值內容的屬性。GADT 的另一個用途是單例型態,其中 GADT 值恰好代表一種型態。此值可以用作此型態的執行時間表示,並且接收它的函式可以具有多型行為。
以下是一個多型函式的範例,該函式採用某種型態 t 的執行時間表示和相同型態的值,然後將該值以字串形式漂亮地列印出來
type _ typ = | Int : int typ | String : string typ | Pair : 'a typ * 'b typ -> ('a * 'b) typ letrec to_string: type t. t typ -> t -> string = fun t x -> match t with | Int -> Int.to_string x | String -> Printf.sprintf "%S" x | Pair(t1,t2) -> let (x1, x2) = x in Printf.sprintf "(%s,%s)" (to_string t1 x1) (to_string t2 x2)
GADT 的另一個常見應用是相等見證。
type (_,_) eq = Eq : ('a,'a) eq let cast : type a b. (a,b) eq -> a -> b = fun Eq x -> x
這裡型態 eq 只有一個建構子,透過在它上面進行匹配,可以添加局部約束,從而允許在 a 和 b 之間進行轉換。透過建立此類相等見證,可以使語法上不同的型態相等。
以下是一個使用單例型態和相等見證來實作動態型態的範例。
letrec eq_type : type a b. a typ -> b typ -> (a,b) eq option = fun a b -> match a, b with | Int, Int -> Some Eq | String, String -> Some Eq | Pair(a1,a2), Pair(b1,b2) -> beginmatch eq_type a1 b1, eq_type a2 b2 with | Some Eq, Some Eq -> Some Eq | _ -> None end | _ -> None type dyn = Dyn : 'a typ * 'a -> dyn let get_dyn : type a. a typ -> dyn -> a option = fun a (Dyn(b,x)) -> match eq_type a b with | None -> None | Some Eq -> Some x
type any = Any : 'name -> any let escape (Any x) = x
Error: 這個表達式的型別是 $name,但預期型別為 'a。型別建構子 $name 會超出其作用域。提示:$name 是由建構子 Any 綁定的存在型別。
$'a 如果存在變數在型別推導期間與型別變數 'a 統一。
type ('arg,'result,'aux) fn = | Fun: ('a ->'b) -> ('a,'b,unit) fn | Mem1: ('a ->'b) * 'a * 'b -> ('a, 'b, 'a * 'b) fn let apply: ('arg,'result, _ ) fn -> 'arg -> 'result = fun f x -> match f with | Fun f -> f x | Mem1 (f,y,fy) -> if x = y then fy else f x