有我的玩具GADT表达:
type _ expr =
| Num : int -> int expr
| Add : int expr * int expr -> int expr
| Sub : int expr * int expr -> int expr
| Mul : int expr * int expr -> int expr
| Div : int expr * int expr -> int expr
| Lt : int expr * int expr -> bool expr
| Gt : int expr * int expr -> bool expr
| And : bool expr * bool expr -> bool expr
| Or : bool expr * bool expr -> bool expr
评估功能:
let rec eval : type a. a expr -> a = function
| Num n -> n
| Add (a, b) -> (eval a) + (eval b)
| Sub (a, b) -> (eval a) - (eval b)
| Mul (a, b) -> (eval a) * (eval b)
| Div (a, b) -> (eval a) / (eval b)
| Lt (a, b) -> (eval a) < (eval b)
| Gt (a, b) -> (eval a) > (eval b)
| And (a, b) -> (eval a) && (eval b)
| Or (a, b) -> (eval a) || (eval b)
当我们限于int expr
:
let create_expr op a b =
match op with
| '+' -> Add (a, b)
| '-' -> Sub (a, b)
| '*' -> Mul (a, b)
| '/' -> Div (a, b)
| _ -> assert false
问题是如何在int expr
函数中支持bool expr
和create_expr
。
我的尝试:
type expr' = Int_expr of int expr | Bool_expr of bool expr
let concrete : type a. a expr -> expr' = function
| Num _ as expr -> Int_expr expr
| Add _ as expr -> Int_expr expr
| Sub _ as expr -> Int_expr expr
| Mul _ as expr -> Int_expr expr
| Div _ as expr -> Int_expr expr
| Lt _ as expr -> Bool_expr expr
| Gt _ as expr -> Bool_expr expr
| And _ as expr -> Bool_expr expr
| Or _ as expr -> Bool_expr expr
let create_expr (type a) op (a:a expr) (b:a expr) : a expr =
match op, concrete a, concrete b with
| '+', Int_expr a, Int_expr b -> Add (a, b)
| '-', Int_expr a, Int_expr b -> Sub (a, b)
| '*', Int_expr a, Int_expr b -> Mul (a, b)
| '/', Int_expr a, Int_expr b -> Div (a, b)
| '<', Int_expr a, Int_expr b -> Lt (a, b)
| '>', Int_expr a, Int_expr b -> Gt (a, b)
| '&', Bool_expr a, Bool_expr b -> And (a, b)
| '|', Bool_expr a, Bool_expr b -> Or (a, b)
| _ -> assert false
它仍然无法返回广义类型的值。
错误:此表达式的类型为
不兼容int expr
但是期望类型为a expr
的表达式 类型int
与类型a
感谢@gsg,我能够实现类型安全评估程序。两个技巧至关重要:
Any
TyInt
和TyBool
),可让我们模式匹配Any
类型见
type _ ty =
| TyInt : int ty
| TyBool : bool ty
type any_expr = Any : 'a ty * 'a expr -> any_expr
let create_expr op a b =
match op, a, b with
| '+', Any (TyInt, a), Any (TyInt, b) -> Any (TyInt, Add (a, b))
| '-', Any (TyInt, a), Any (TyInt, b) -> Any (TyInt, Sub (a, b))
| '*', Any (TyInt, a), Any (TyInt, b) -> Any (TyInt, Mul (a, b))
| '/', Any (TyInt, a), Any (TyInt, b) -> Any (TyInt, Div (a, b))
| '<', Any (TyInt, a), Any (TyInt, b) -> Any (TyBool, Lt (a, b))
| '>', Any (TyInt, a), Any (TyInt, b) -> Any (TyBool, Gt (a, b))
| '&', Any (TyBool, a), Any (TyBool, b) -> Any (TyBool, And (a, b))
| '|', Any (TyBool, a), Any (TyBool, b) -> Any (TyBool, Or (a, b))
| _, _, _ -> assert false
let eval_any : any_expr -> [> `Int of int | `Bool of bool] = function
| Any (TyInt, expr) -> `Int (eval expr)
| Any (TyBool, expr) -> `Bool (eval expr)
答案 0 :(得分:5)
正如您所发现的,此方法不会进行类型检查。它还有一个更基本的问题:GADT可以是递归的,在这种情况下,根本不可能枚举它们的情况。
相反,您可以将类型统一为GADT并传递它们。这是一个简单的例子:
type _ expr =
| Num : int -> int expr
| Add : int expr * int expr -> int expr
| Lt : int expr * int expr -> bool expr
| And : bool expr * bool expr -> bool expr
type _ ty =
| TyInt : int ty
| TyBool : bool ty
let bin_op (type a) (type b) op (l : a expr) (r : a expr) (arg_ty : a ty) (ret_ty : b ty) : b expr =
match op, arg_ty, ret_ty with
| '+', TyInt, TyInt -> Add (l, r)
| '<', TyInt, TyBool -> Lt (l, r)
| '&', TyBool, TyBool -> And (l, r)
| _, _, _ -> assert false
在某些时候,您将希望拥有一个可以是“任何表达式”的值。介绍一个存在的包装器允许这样做。俗气的例子:生成随机表达式树:
type any_expr = Any : _ expr -> any_expr
let rec random_int_expr () =
if Random.bool () then Num (Random.int max_int)
else Add (random_int_expr (), random_int_expr ())
let rec random_bool_expr () =
if Random.bool () then Lt (Num (Random.int max_int), Num (Random.int max_int))
else And (random_bool_expr (), random_bool_expr ())
let random_expr () =
if Random.bool () then Any (random_int_expr ())
else Any (random_bool_expr ())
答案 1 :(得分:0)
create_expr
所声明的类型为char -> 'a expr -> 'a expr -> 'a expr
。但'>'
案例的类型为char -> int expr -> int expr -> bool expr
。所以基本计划似乎存在问题。
实质上,您希望结果的类型取决于角色的值。我不是绝对肯定的,但我怀疑这在OCaml中是不可能的。似乎它需要一个更强大的类型系统。