Interfaces in OCaml

OCaml uses modules to organize code, and interfaces are implemented using modules and signatures. Here’s how we can translate the Go example to OCaml:

open Printf

(* Here's a basic interface (signature) for geometric shapes. *)
module type Geometry = sig
  val area : unit -> float
  val perim : unit -> float
end

(* For our example we'll implement this interface on rect and circle types. *)
module Rect = struct
  type t = {width: float; height: float}

  (* To implement an interface in OCaml, we just need to
     implement all the functions in the signature. *)
  let area {width; height} = width *. height
  let perim {width; height} = 2.0 *. width +. 2.0 *. height
end

module Circle = struct
  type t = {radius: float}

  (* The implementation for circles. *)
  let area {radius} = Float.pi *. radius *. radius
  let perim {radius} = 2.0 *. Float.pi *. radius
end

(* If a module has the Geometry signature, then we can use it
   with functions that expect that signature. Here's a generic
   measure function taking advantage of this to work on any Geometry. *)
let measure (module G : Geometry) =
  printf "Area: %f\n" (G.area ());
  printf "Perimeter: %f\n" (G.perim ())

(* We can create modules that conform to the Geometry signature *)
module RectGeometry (R : sig val rect : Rect.t end) : Geometry = struct
  let area () = Rect.area R.rect
  let perim () = Rect.perim R.rect
end

module CircleGeometry (C : sig val circle : Circle.t end) : Geometry = struct
  let area () = Circle.area C.circle
  let perim () = Circle.perim C.circle
end

let () =
  let r = Rect.{width = 3.0; height = 4.0} in
  let c = Circle.{radius = 5.0} in

  (* The Circle and Rect modules both implement the Geometry signature
     so we can use instances of these modules as arguments to measure. *)
  let module R = RectGeometry(struct let rect = r end) in
  let module C = CircleGeometry(struct let circle = c end) in
  
  measure (module R);
  measure (module C)

To run this program, save it as interfaces.ml and compile it with OCaml:

$ ocamlc -o interfaces interfaces.ml
$ ./interfaces
Area: 12.000000
Perimeter: 14.000000
Area: 78.539816
Perimeter: 31.415927

In this OCaml version:

  1. We define a Geometry module type (signature) which is equivalent to the Go interface.
  2. We implement Rect and Circle modules with their respective types and functions.
  3. We create a measure function that takes a module conforming to the Geometry signature.
  4. We use functors (RectGeometry and CircleGeometry) to create modules that implement the Geometry signature for our Rect and Circle types.
  5. In the main function, we create instances of Rect and Circle, wrap them in modules that implement Geometry, and pass these to the measure function.

This demonstrates how OCaml uses modules and signatures to achieve similar functionality to Go’s interfaces, albeit with a different syntax and approach.