我正在尝试模拟OCaml中的接口并使用“type”构造。我有两种类型:
type fooSansBar = {a: string; b: int};;
type fooConBar = {a:string; b:int; bar:char};;
...并且想要定义一个特定的fooSansBar:
let fsb = {a="a"; b=3};;
...但是我被告知没有定义条形字段。由此看来,与我在匹配fooSansBar签名时传递的值相反,系统认为我正在尝试创建一个fooConBar。如果存在上面定义的两种类型,是否可以创建一个fooSansBar?
另外(因为我是OCaml的新手)有更好的方法来模拟界面吗?
答案 0 :(得分: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
使用模块和模块类型,您也可以获得子类型;没有必要求助于物体。
P.S。我的Caml生锈了;语法可能已关闭。
答案 1 :(得分: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)
答案 2 :(得分:3)
第二种类型重新定义了a和b,有效地隐藏了第一种,这就是为什么它不能再构造了。您可以在不同的模块中定义这些类型,但这与使用a和b的不同名称相同。
这些构造只能在您不尝试从其他接口“派生”时使用,而只是实现它。
如果您希望在Ocaml中使用这些面向对象的概念,您可以查看对象系统,或者,根据您的问题,查看模块系统。或者,您可以尝试以功能方式解决您的问题。你想解决什么问题?
答案 3 :(得分: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
这样您就可以在需要fooConBar
的任何地方使用fooSansBar
。您现在可以使用类型推断创建fooSansBar
:
let fsb = object
method a = "a"
method b = 3
end
现在,fsb
的类型恰好是<a: string; b: int>
,如Jon所示,但由于OCaml的结构子类型,它完全可用作fooSansBar
。
答案 4 :(得分: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