我有一个参数化类型,它递归地使用自己但是有一个专门的类型参数,当我实现一个泛型运算符时,由于处理专用子树的情况,该运算符的类型绑定得太紧。第一个代码示例显示了问题,第二个示例显示了我不想使用的解决方法,因为实际代码有更多的情况,因此以这种方式复制代码会造成维护危险。
这是一个显示问题的最小测试用例:
module Op1 = struct
type 'a t = A | B (* 'a is unused but it and the _ below satisfy a sig *)
let map _ x = match x with
| A -> A
| B -> B
end
module type SIG = sig
type ('a, 'b) t =
| Leaf of 'a * 'b
(* Here a generic ('a, 'b) t contains a specialized ('a, 'a Op1.t) t. *)
| Inner of 'a * ('a, 'a Op1.t) t * ('a, 'b) t
val map : ('a -> 'b) -> ('a_t -> 'b_t) -> ('a, 'a_t) t -> ('b, 'b_t) t
end
module Impl : SIG = struct
type ('a, 'b) t =
| Leaf of 'a * 'b
| Inner of 'a * ('a, 'a Op1.t) t * ('a, 'b) t
(* Fails signature check:
Values do not match:
val map :
('a -> 'b) ->
('a Op1.t -> 'b Op1.t) -> ('a, 'a Op1.t) t -> ('b, 'b Op1.t) t
is not included in
val map :
('a -> 'b) -> ('a_t -> 'b_t) -> ('a, 'a_t) t -> ('b, 'b_t) t
*)
let rec map f g n = match n with
| Leaf (a, b) -> Leaf (f a, g b)
(* possibly because rec call is applied to specialized sub-tree *)
| Inner (a, x, y) -> Inner (f a, map f (Op1.map f) x, map f g y)
end
Impl.map
的这个修改版本修复了问题但引入了维护危险。
let rec map f g n = match n with
| Leaf (a, b) -> Leaf (f a, g b)
| Inner (a, x, y) -> Inner (f a, map_spec f x, map f g y)
and map_spec f n = match n with
| Leaf (a, b) -> Leaf (f a, Op1.map f b)
| Inner (a, x, y) -> Inner (f a, map_spec f x, map_spec f y)
有没有办法让这个工作没有重复let rec map
的主体?
应用gasche的解决方案会产生以下工作代码:
let rec map
: 'a 'b 'c 'd . ('a -> 'b) -> ('c -> 'd) -> ('a, 'c) t -> ('b, 'd) t
= fun f g n -> match n with
| Leaf (a, b) -> Leaf (f a, g b)
| Inner (a, x, y) -> Inner (f a, map f (Op1.map f) x, map f g y)
答案 0 :(得分:5)
数据类型定义中的这种递归方式称为"非常规":递归类型'a t
在foo t
不同于foo
的实例中重用定义中使用的单个变量'a
。另一个众所周知的例子是完整二叉树的类型(正好有2 ^ n个叶子):
type 'a full_tree =
| Leaf of 'a
| Node of ('a * 'a) full_tree
运行这些数据类型的递归函数通常会遇到具有类型推断的语言的单态递归限制。当你进行类型推断时,你必须先猜测一个递归函数的类型,然后再对它的体进行类型检查(因为它可能在里面使用)。 ML语言通过统一/推理来改进这种猜测,但是只能推断出单态类型。如果你的函数对它自己进行多态使用(它在一个不同的类型上递归地调用它自己作为输入),这是无法推断的(在一般情况下它是不可判定的)。
let rec depth = function
| Leaf _ -> 1
| Node t -> 1 + depth t
^
Error: This expression has type ('a * 'a) full_tree
but an expression was expected of type 'a full_tree
从3.12开始,OCaml允许使用显式的多态注释
表单'a 'b . foo
,表示forall 'a 'b. foo
:
let rec depth : 'a . 'a full_tree -> int = function
| Leaf _ -> 1
| Node t -> 1 + depth t
您可以在示例中执行相同的操作。但是,我无法做到
使用模块中的注释后编译该类型
签名,因为它似乎是错误的('a_t
只是奇怪的)。这里
是我过去的工作:
let rec map : 'a 'b . ('a -> 'b) -> ('a Op1.t -> 'b Op1.t) ->
('a, 'a Op1.t) t -> ('b, 'b Op1.t) t
= fun f g n -> match n with
| Leaf (a, b) -> Leaf (f a, g b)
| Inner (a, x, y) -> Inner (f a, map f (Op1.map f) x, map f g y)