Reputation: 2362
I am attempting to simulate an interface in OCaml and am using the "type" construct. I have two types:
type fooSansBar = {a: string; b: int};;
type fooConBar = {a:string; b:int; bar:char};;
...and would like to define a particular fooSansBar
:
let fsb = {a="a"; b=3};;
...but am told that the bar
field is not defined. From this, it appears that, contrary to the values I passed in matching fooSansBar
's signature, the system believes I am trying to create a fooConBar
. Is it possible to create a fooSansBar
if the two types as defined above exist?
Additionally (because I'm new to OCaml) is there a better way to simulate an interface?
Upvotes: 9
Views: 773
Reputation: 29100
OCaml provides two ways to implement interfaces. One, as already mentioned, is a module type.
The other is a class type. You can write a class type (interface) fooSansBar
:
class type fooSansBar = object
method a: string
method b: int
end
and a class type fooConBar:
class type fooConBar = object
inherit fooSansBar
method bar: char
end
This will allow you to use a fooConBar
anywhere a fooSansBar
is required. You can now create a fooSansBar
, using type inference:
let fsb = object
method a = "a"
method b = 3
end
Now, fsb
's type happens to be <a: string; b: int>
, as indicated by Jon, but it's perfectly usable as a fooSansBar
due to OCaml's structural subtyping.
Upvotes: 2
Reputation: 285
In OCaml, it's not possible to have two record types with intersecting field sets present in the same scope.
If you really need to use record types with intersecting field sets, then you can work around this restriction by enclosing the types within their own dedicated modules:
module FooSansBar = struct type t = {a:string; b:int} end
module FooConBar = struct type t = {a:string; b:int; bar:char} end
Then you can construct instances of these types like so:
let fsb = {FooSansBar.a="a"; b=3}
let fcb = {FooConBar.a="a"; b=4; bar='c'}
These instances have the following types:
fsb : FooSansBar.t
fcb : FooConBar.t
Upvotes: 1
Reputation: 48687
There are several possible solutions in OCaml depending how you're using the code you gave. The simplest is to combine the two types:
type fooBar = { a: string; b: int; bar: char option }
Another solution is to replace the records with objects because objects support subtyping (and can have their types inferred so there is no need to declare a type!):
# 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)
Upvotes: 4
Reputation: 202595
In OCaml, field names in record types must be unique, so the two types you define cannot coexist simultaneously. Caml is the only language I know with this property.
Because the second definition hides the first, when the compiler sees the a and b fields it expects them to belong to the fooConBar
type and so complains of the missing bar field.
If you are trying to simulate an interface, the correct functional way to do it in Caml is to define a module type
.
module type FOO_CON_BAR = sig
val a : string
val b : int
val bar : char
end
And an instance:
module Example = struct
let a = "hello"
let b = 99
let c = '\n'
end
With modules and module types you also get subtyping; there's no need to resort to objects.
P.S. My Caml is rusty; syntax may be off.
Upvotes: 9
Reputation: 3094
The second type redefines a and b, effectively hiding the first, which is why it cannot be constructed any more. You could define these types in different modules, but that would be the same as using a different name for a and b.
These constructs can only be used when you do not try to "derive" from another interface, but just implement it.
If you wish to use these object oriented concepts in Ocaml, you could look at the object system, or, depending on your problem, the module system. Alternatively, you could try to solve your problem in a functional way. What problem are you trying to solve?
Upvotes: 3