Types 在调用函数和回调函数之间对齐多态变量类型

Types 在调用函数和回调函数之间对齐多态变量类型,types,ocaml,algebraic-data-types,subtyping,Types,Ocaml,Algebraic Data Types,Subtyping,我正在尝试编写一个不需要处理所有已知类型事件的事件处理程序,并尝试使用OCaml多态变量类型(event.mli)对此进行建模: 不幸的是,此操作失败,出现以下错误: File "event.ml", line 1: Error: The implementation event.ml does not match the interface event.cmi: Values do not match: val mainLoop :

我正在尝试编写一个不需要处理所有已知类型事件的事件处理程序,并尝试使用OCaml多态变量类型(
event.mli
)对此进行建模:

不幸的是,此操作失败,出现以下错误:

File "event.ml", line 1:
Error: The implementation event.ml
       does not match the interface event.cmi:
       Values do not match:
         val mainLoop :
           ([> `click of int * int | `keypress of char | `quit of int ] -> 'a) ->
           unit
       is not included in
         val mainLoop :
           ([< `click of int * int | `keypress of char | `quit of int ] event ->
            unit) ->
           unit
       File "event.ml", line 4, characters 4-12: Actual declaration
文件“event.ml”,第1行:
错误:implementation event.ml
与接口event.cmi不匹配:
值不匹配:
val mainLoop:
([>`click of int*int |`keypress of char | `quit of int]->'a)->
单元
不包括在
val mainLoop:
([<`click of int*int | `keypress of char | `quit of int]事件->
单位)->
单元
文件“event.ml”,第4行,字符4-12:实际声明

如何将
mainLoop
的实现推断为类型
([<`click of int*int | ` keypress of char | ` quit of int]->unit)->单元
,即
('events event->unit)->单元

我认为问题在于您的类型定义,我知道您希望您的类型最多包含这三个事件(首先,为什么是“最多”而不是“至少”),但在这种情况下,如果签名为
mainLoop
,您就无法预测您的类型

例如,看看x的类型:

let (x : [< `A | `B]) = `A
val x : [< `A | `B > `A ] = `A
但这真的是个问题吗?为什么不将
type'events event=[…
更改为
type'events event=[…

在我看来,使用下限比使用上限更好:

type 'events event =
  [> `click of int * int | `keypress of char | `quit of int ] as 'events

val mainLoop : ('events event -> unit) -> unit

val handler : 'events event -> unit

let mainLoop handler =
  for n = 1 to 10 do
    handler (
      if n mod 2 = 0 then `click (n, n + 1)
      else if n mod 3 = 0 then `keypress 'x'
      else `quit 0
    )
  done

let handler = function
  | `click (x, y) -> Printf.printf "Click x: %d, y: %d\n" x y
  | `quit code -> exit code
  | _ -> ()

让我们用通俗易懂的英语来解释一下“亚类型理论”,以及一些常识

在面向对象语言(如java或ocaml)中,您可以定义的最通用类是空类,即没有属性或方法的类。实际上,任何类都可以从中派生,就类型而言,任何类类型都是空类类型的子类型

现在,它们的输入是逆变的,输出是协变的

如果我们观察函数的类型行为,我们会发现:

  • 您可以向它传递它接受的类型的值,或该类型的任何子类型的值。在极端情况下,如果您定义一个函数接受空类的实例,显然该函数将无法对其执行任何操作。因此,任何其他类的实例也将执行此操作,因为我们知道该函数不会期望任何类型的值当然可以
  • 函数的结果可以是它定义为返回的类型的值,也可以是该类型的任何子类型的值
为什么我们用两个不同的词来表示输入和输出的行为似乎相同

嗯,现在考虑ML风格类型理论中常见的类型构造函数:产品(<代码> */Cuth>类型构造函数)、和(代数数据类型)和箭头(函数类型)。基本上,如果使用产品或总和定义类型T,则专门(使用子类型)它们的任何参数都会产生专门化(子类型)。例如,由和组成的列表构造函数是协变的:如果你有一个类

a
的列表,而
b
是从
a
派生出来的,那么类型
b list
a list
的一个子类型。实际上,对于接受
a list
的函数,你可以通过it a
b列表
无错误

如果我们看一下箭头
->
构造函数,情况略有不同。
x->y
类型的函数f接受
x
的任何子类型,并返回
y
的任何子类型。如果我们假设x是一个函数类型,这意味着f实际上有
(u->v)类型->y
,带有
x
=
u->v
。到目前为止还不错。
u
v
在这个设置中有什么变化?这取决于f可能对它做什么。f知道它只能将
u
u
的子类型传递给该函数,因此f可以传递的最一般值是a
u
,这意味着在极端情况下,我可以给f一个函数,它接受空对象作为它的参数,并返回类型为
v
。因此,突然之间,类型集从“类型和子类型”变为“类型和超类型”。因此类型
u->v
u'->v'
,其中
v'
v
的子类型,
u'
u
的超类型。这就是为什么我们的箭头构造函数在其输入上被称为逆变。类型构造函数的变化是根据子类型/super>确定其子类型/超类型的方式它的参数类型

下面,我们必须考虑多态变体。如果类型<代码> x <代码>被定义为<代码> [A```b] < /代码>,那么与类型<代码> y>代码>=代码> [a] < /代码>?在这里,
`A
属于两种类型,因此强制转换在两种方式下都是安全的,但是
`B
仅出现在第一种类型中,因此可能不会被强制转换到第二种类型。 因此,
y
的值可以强制转换到第一个,但是
x
的值不能强制转换到第二个。子类型关系很清楚:
y
x
的子类型

那么
[>…]
[<…]
符号呢?第一个符号表示一个类型及其所有超类型(有无限多个超类型),而第二个符号表示一个类型及其所有子类型(这是一个有限集,包含空类型)因此,对于采用多态变量的函数,自然推断出的类型将在输入该变量及其所有子类型时出现,即
[let (x : [< `A | `B]) = `A
val x : [< `A | `B > `A ] = `A
let mainLoop (handler :
              [< `click of int * int | `keypress of char | `quit of int ]
              event -> unit) = ...

       Values do not match:
         val mainLoop :
           ([ `click of int * int | `keypress of char | `quit of int ] event ->
            unit) ->
           unit
       is not included in
         val mainLoop :
           ([< `click of int * int | `keypress of char | `quit of int ] event ->
            unit) ->
           unit
type 'events event =
  [> `click of int * int | `keypress of char | `quit of int ] as 'events

val mainLoop : ('events event -> unit) -> unit

val handler : 'events event -> unit

let mainLoop handler =
  for n = 1 to 10 do
    handler (
      if n mod 2 = 0 then `click (n, n + 1)
      else if n mod 3 = 0 then `keypress 'x'
      else `quit 0
    )
  done

let handler = function
  | `click (x, y) -> Printf.printf "Click x: %d, y: %d\n" x y
  | `quit code -> exit code
  | _ -> ()
type basic_event =
   [ `click of int * int | `keypress of char | `quit of int ]

(* Main loop should take an event handler callback that handles 'less than' 
   all known event types *)
val mainLoop : ([> basic_event ] -> unit) -> unit

val handler : [< `click of int * int | `quit of int ] -> unit