Ocaml 从外面看

Ocaml 从外面看,ocaml,gadt,locally-abstract-type,Ocaml,Gadt,Locally Abstract Type,GADT允许某种形式的动态键入: type _ gadt = | Int: int -> int gadt | Float: float -> float gadt let f: type a. a gadt -> a = function | Int x -> x + 1 | Float x -> x +. 1. 我希望能够执行相同类型的分派,但是使用参数化类型,从外部访问gadt的参数。 如果参数是普遍量化的或固定的,这很容易: type (

GADT允许某种形式的动态键入:

type _ gadt =
  | Int: int -> int gadt
  | Float: float -> float gadt

let f: type a. a gadt -> a = function
  | Int x -> x + 1
  | Float x -> x +. 1.
我希望能够执行相同类型的分派,但是使用参数化类型,从外部访问gadt的参数。 如果参数是普遍量化的或固定的,这很容易:

type (_,_) container = 
  | List: 'a list -> ('a,'a list) container

let f : type a b. (a,b) container -> b = fun (List l) -> l
let g : type a b. a -> (a, b) container -> b = fun x (List l) -> x::l
let h : type b. (int, b) container -> b = fun (List l) ->  1::l
但是,如果参数上存在其他形式的约束,则不起作用:

class ['a] ko (x:'a) = object 
    method m : type b. ('a, b) container -> b = fun (List l) -> x::l
end
我得到了一个错误:类型构造函数a#0将脱离其作用域。 我猜这是由于由外而内的限制,没有完全理解这个领域

我找到的唯一解决方案是使用更高的模块:

open Higher
module Higher_List = Newtype1 (struct type 'a t = 'a list end)
type ('a,_) container = List: 'a list -> ('a, Higher_List.t) container

class ['a] c (x:'a) = object
    method m : type b. b container -> ('a,b) app = fun (List l) -> Higher_List.inj(x::l)
end
然而,这个解决方案远非完美:首先,它是冗长的,到处都是inj和prj,更重要的是,它有很多限制:“a参数不能有约束,也不能有方差

有人知道更好的解决方案吗

编辑

经过一番思考,Drup解决方案正在发挥作用,但必须小心!在我的完整问题(不是这个问题中的玩具程序)中,我需要在方法定义中访问self。所以回到Drup解决方案,我必须将self传递给中间函数,为此,我必须给它一个类型。所以我必须首先声明一个类类型

class type ['a] c_type = object
    method m: ('a, 'b) container -> 'b
end

let cons : type a b. a c_type -> a -> (a,b) container  -> b =
    fun self x (List l) -> x::l

class ['a] c (x:'a) = object(self: 'a #c_type)
    method m = cons (self :> 'a c_type) x
end
但是,如果类
c
'a
有约束,这将不起作用:
cons
的类型将无效,因为
a
必须普遍量化,但在
a c\u类型中有一个约束。解决方案是编写
c_type
,而不受
'a
的约束。但是请注意,这意味着大量的重写。在许多情况下,仅仅省略约束并不足以使其消失:它的所有用法都必须是无约束的

因此,最终的解决方案如下所示:

type (_,_) container = 
  | List: 'a list -> ('a,'a list) container

class type ['a] c_type = object
    (* no constraint on 'a, and take care of its usage ! *)
    method m: ('a, 'b) container -> 'b
end

let cons : type a b. a c_type -> a -> (a,b) container  -> b =
    fun self x (List l) -> x::l (* in real code, self is used... *)

class ['a] c (x:'a) = object(self: 'a #c_type)
    constraint 'a = < .. > (* well just for the example *)
    method m = cons (self :> 'a c_type) x
end
type(u,u)容器=
|List:'a List->('a,'a List)容器
类类型['a]c_类型=对象
(*对“a”没有限制,注意它的用法!)
方法m:('a,'b)容器->'b
结束
让我们来看看a型和b型。a集装箱类型->a->(a,b)集装箱->b=
fun self x(列表l)->x::l(*在实际代码中,使用self…*)
类['a]c(x:'a)=对象(self:'a#c#u类型)
约束'a=<…>(*仅举个例子*)
方法m=cons(自身:>“c_类型”)x
结束

既然你能做简单的事,为什么还要尝试复杂呢?:)

这种构造很复杂,因为局部抽象类型的位置很重要。在您的情况下,您希望在类的外层有一个本地抽象类型(对于类型
a
),这是不可能的。制作一个适当抽象的中间函数通常会有所帮助


另外,不要使用更高的
。这证明了你可以在模块系统中对HKT进行编码,而不是鼓励你真正这样做。

这个更高的模块是从哪里来的。作为
Higher
的作者,我不同意这仅仅是一个概念证明。它被设计用来使用——只是不经常使用。在一些用例中,使用
higher
会产生比使用functor更干净的代码——有关一些示例,请参阅。
type (_,_) container =
  | List: 'a list -> ('a,'a list) container

let cons : type a b. a -> (a, b) container -> b = fun x (List l) -> x::l

class ['a] ko (x:'a) = object
    method m : type b. ('a, b) container -> b = cons x
end