Mat Kelly
Mat Kelly

Reputation: 2362

OCaml types with different levels of specificity

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

Answers (5)

Michael Ekstrand
Michael Ekstrand

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

jsk
jsk

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

J D
J D

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

Norman Ramsey
Norman Ramsey

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

small_duck
small_duck

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

Related Questions