9

我正在尝试在 OCaml 中模拟一个接口并使用“类型”构造。我有两种类型:

type fooSansBar = {a: string; b: int};;
type fooConBar = {a:string; b:int; bar:char};;

...并想定义一个特定的 fooSansBar:

let fsb = {a="a"; b=3};;

...但被告知 bar 字段未定义。由此看来,与我在匹配 fooSansBar 的签名中传递的值相反,系统认为我正在尝试创建一个 fooConBar。如果存在上面定义的两种类型,是否可以创建 fooSansBar?

此外(因为我是 OCaml 的新手)是否有更好的方法来模拟界面?

4

5 回答 5

9

在 OCaml 中,记录类型中的字段名称必须是唯一的,因此您定义的两种类型不能同时共存。Caml 是我知道的唯一具有此属性的语言。

因为第二个定义隐藏了第一个,所以当编译器看到 a 和 b 字段时,它希望它们属于该fooConBar类型,因此抱怨缺少 bar 字段。

如果您尝试模拟接口,在 Caml 中执行此操作的正确功能方法是定义一个module type.

module type FOO_CON_BAR = sig
  val a : string
  val b : int
  val bar : char
end

还有一个例子:

module Example = struct
  let a = "hello"
  let b = 99
  let c = '\n'
end

使用模块和模块类型,您还可以获得子类型;没有必要求助于对象。

PS 我的 Caml 生锈了;语法可能已关闭。

于 2009-03-21T23:34:51.877 回答
4

OCaml 中有几种可能的解决方案,具体取决于您如何使用您提供的代码。最简单的是将两种类型结合起来:

type fooBar = { a: string; b: int; bar: char option }

另一种解决方案是用对象替换记录,因为对象支持子类型化(并且可以推断出它们的类型,因此无需声明类型!):

# let fsb = object
    method a = "a"
    method b = 3
  end;;
val fsb : < a : string; b : int > = <obj>

# fsb#a, fsb#b;;
- : string * int = ("a", 3)
于 2009-05-06T12:01:34.563 回答
3

第二种类型重新定义了 a 和 b,有效地隐藏了第一种,这就是它不能再构造的原因。您可以在不同的模块中定义这些类型,但这与为 a 和 b 使用不同的名称相同。

只有当您不尝试从另一个接口“派生”而只是实现它时,才能使用这些构造。

如果您希望在 Ocaml 中使用这些面向对象的概念,您可以查看对象系统,或者,根据您的问题,模块系统。或者,您可以尝试以实用的方式解决您的问题。你想解决什么问题?

于 2009-03-21T18:10:48.320 回答
2

OCaml 提供了两种实现接口的方法。如前所述,一种是模块类型。

另一种是类类型。您可以编写一个类类型(接口)fooSansBar

class type fooSansBar = object
    method a: string
    method b: int
end

和一个类类型 fooConBar:

class type fooConBar = object
    inherit fooSansBar
    method bar: char
end

这将允许您在需要 a 的fooConBar任何地方使用 a fooSansBar。您现在可以创建一个fooSansBar, 使用类型推断:

let fsb = object
    method a = "a"
    method b = 3
end

现在,fsb' 的类型恰好是<a: string; b: int>,正如 Jon 所指出的那样,但fooSansBar由于 OCaml 的结构子类型,它完全可以用作 a 。

于 2010-11-23T22:32:00.773 回答
1

在 OCaml 中,不可能有两种具有相交字段集的记录类型存在于同一范围内。

如果您确实需要使用具有相交字段集的记录类型,则可以通过将类型包含在它们自己的专用模块中来解决此限制:

module FooSansBar = struct type t = {a:string; b:int} end
module FooConBar = struct type t = {a:string; b:int; bar:char} end

然后你可以像这样构造这些类型的实例:

let fsb = {FooSansBar.a="a"; b=3}
let fcb = {FooConBar.a="a"; b=4; bar='c'}

这些实例具有以下类型:

fsb : FooSansBar.t 
fcb : FooConBar.t
于 2010-11-23T07:55:31.047 回答