OCaml';s GADT作为执行级别的参数
我试图编写一个函数OCaml';s GADT作为执行级别的参数,ocaml,gadt,Ocaml,Gadt,我试图编写一个函数run,使用一个参数来参数化它的执行级别。我希望此函数在给定级别后返回其输出。我使用GADTs让的输出运行取决于它的输入 代码如下: type _ level_output = | FirstO : int -> int level_out
run
,使用一个参数来参数化它的执行级别。我希望此函数在给定级别后返回其输出。我使用GADTs让的输出运行
取决于它的输入
代码如下:
type _ level_output =
| FirstO : int -> int level_output
| SecondO : float -> float level_output
| ThirdO : string -> string level_output
type _ run_level_g =
| First : int run_level_g
| Second : float run_level_g
| Third : string run_level_g
type run_level = Any : 'a run_level_g -> run_level
let first _ =
(*do stuff*)
1
let second _ =
(*do stuff*)
2.5
let third _ =
(*do stuff*)
"third"
let run1 (type a) (level:a run_level_g) data : a level_output =
let out = first data in
match level with
| First -> FirstO out
| Second ->
let out = second out in
SecondO out
| Third ->
let out = second out in
let out = third out in
ThirdO out
let run2 (type a) (level:a run_level_g) data : a level_output =
let out = first data in
if Any level = Any First
then FirstO out
else
let out = second out in
if Any level = Any Second
then SecondO out
else
let out = third out in
ThirdO out
type (_,_) eq = Eq : ('a,'a) eq
let eq_level (type a b) (x:a run_level_g) (y:b run_level_g) : (a, b) eq option =
match x, y with
| First, First -> Some Eq
| Second, Second -> Some Eq
| Third, Third -> Some Eq
| _ -> None
let cast_output (type a b) (Eq:(a, b) eq) (v:a level_output) : b level_output = v
let run3 (type a) (level:a run_level_g) data : a level_output =
let out = first data in
let eq = eq_level First level in
match eq with
| Some eq -> cast_output eq (FirstO out)
| None ->
let out = second out in
let eq = eq_level Second level in
match eq with
| Some eq -> cast_output eq (SecondO out)
| None ->
let out = third out in
let eq = eq_level Third level in
match eq with
| Some eq -> cast_output eq (ThirdO out)
| None -> failwith "this can't happen"
有三个版本的run
。第一个很好,但有代码重复,我想删除它。我希望我的函数看起来更像run2
,但是这个函数没有编译,因为类型检查器无法从if条件推断类型。这个问题的答案是run3
,但现在我遇到了这个笨重的failwith
案例,显然不可能发生
我想知道是否有一种方法可以让我做到两全其美,一个没有代码重复和案例失败的函数?我发现你的函数
run1
是迄今为止可读性最强的函数。
消除一些代码重复的一种可能是使run1递归
首先,可以定义一个简短的辅助函数来从level_输出中提取数据
let proj (type a) (x:a level_output): a =
match x with
| FirstO x -> x
| SecondO x -> x
| ThirdO x -> x;;
然后,run的递归变量可以写成
let rec run: type a. a run_level_g -> 'b -> a level_output =
fun level data -> match level with
| First -> FirstO(first data)
| Second -> SecondO(second @@ proj @@ run First data)
| Third -> ThirdO(third @@ proj @@ run Second data);;
但你为什么使用GADT?我的意思是,如果
level\u output
和run\u level\u g
只是简单的代数类型,那么您的run2
就可以工作了。有什么理由使用GADT吗?我不想在我确切知道将返回的类型后,在我使用的每个位置都解包level_输出
。这将在呼叫位置创建一组failwith
案例。为什么将run的声明替换为let rec run(类型a)(级别:a run\u level\u g)数据:一个level_输出
会导致类型错误?因为run
函数是递归多态的:它在其定义中被调用,并使用其类型参数的不同实例()。因此,有必要为函数类型添加一个明确的通用量化。这里,类型a
是一个简短的表示法,它将类型声明为通用量化(对于多态递归)和局部抽象(对于GADT模式匹配的类型细化).您知道有什么方法可以声明类型a
是通用的量化和局部抽象的,适合于内联类型信息的声明。与用于proj
而不是run
的一样,在这种情况下,这实际上是不可能的:通用量化应用于类型表达式,因此您至少需要有'a'运行级别\u g->\u->'a级别\u输出
,因为您需要表示级别\u输出中的'a
与'a运行级别\u g
中普遍量化的相同。您可以内联地重复类型信息,但我不确定它是否带来很多好处。