The shape design problem

For a practical use-case here, I would try to resist the temptation to reach for the big
guns — GADTS, first-class-modules, polymorphic variants etc.

Simple records come a long way. First, defining a `Shape` module (the type `t`

``````module Shape = struct
type t = { area : float ; draw : unit -> unit }
let make ~area ~draw = { area; draw }
let draw_all = List.iter (fun s -> s.draw ())
end
``````

Second, define the `Shape` instances as separate modules where each module exposes
a `to_shape` function:

``````module Point = struct
type t = { x : float; y : float}
let make ~x ~y = { x; y }
let to_shape { x; y } =
Shape.make ~area:0. ~draw:(fun () -> Printf.printf "Point (%f,%f)" x y)
end

module Rectangle = struct
type t = { bottom_left : Point.t; bottom_right : Point.t }
let make ~bottom_left ~bottom_right = { bottom_left; bottom_right }
let to_shape { bottom_left; bottom_right } = failwith "TODO"
end
``````

To create some shapes and bundle them together:

``````let my_point = Point.make ~x:10. ~y:20.

let my_rectangle =
Rectangle.make
~bottom_left:(Point.make ~x:1. ~y:1.)
~bottom_right:(Point.make ~x:4. ~y:5.)

let my_shapes = [ Point.to_shape my_point; Rectangle.to_shape my_rectangle ]

let () = Shape.draw_all my_shapes
``````

Sure, you need to lift each instance manually using the `to_shape` functions, but the solutions
where you pack a first-class module along with a value also require lifting.

3 Likes