In my program, I have to deal with three kinds of things: orders, which can be carrot orders or milk orders. All three types will appear as arguments of some later functions. My current setup is:
utop # type order = C of carrot_order | M of milk_order
and carrot_order = {client: string; number: int}
and milk_order = {client: string; volume: float};;
utop # let order_client = function
C {client; _} | M {client; _} -> client;;
val order_client : order -> string = <fun>
This is obviously a toy example, and there are other fields for each type. This permits me to write the later functions that I need. (A more or less equivalent way would be to “factor out” the client
field, see below.)
However, there is a feel of redundancy in the code above, and there is a collision for the field name client
(the compiler warns: (warning 30 [duplicate-definitions])
). Not sure if this is bad. Also, I do not know if the “double pun” I use in the function order_client
is idiomatic to OCaml or on the contrary is bad practice. My first idea, which seemed natural coming from Python, was to use objects and subtyping or row polymorphism instead. But I seem to understand that objects are not much used in OCaml, and should be used mostly when one needs open recursion (Objects - Real World OCaml), which is not the case here.
So, should I be content with my current solution (or the solution below), or should I use objects, or yet another paradigm ?
Another possibility: factor out the client
field, and define a function which constructs an order from a client
and a bare_order
:
utop # type order = {client: string; bare_order: bare_order}
and bare_order = Cb of bare_carrot_order | Mb of bare_milk_order
and bare_carrot_order = {number: int}
and bare_milk_order = {volume: float};;
utop # let order_from_bare_order client bare_order = {client; bare_order};;
val order_from_bare_order : string -> bare_order -> order = <fun>
This also looks a bit verbose.