I’m trying to implement an approach, where several related record types are inserted into a data structure. Since the record types are related, I am using classes and inheritance to form the desired set of fields.
The data structure works as follows:
type t (* data structure *)
type id (* basically, a key *)
module R = struct
class record1 = object (* ... *) end
class record2 = object (* ... *) end
class record3 = object (* ... *) end
(* etc. etc. etc. *)
end
(* tagged variant of various object types
representing various records *)
type item =
| Record1 of R.record1
| Record2 of R.record2
| Record2 of R.record2
(* etc. etc. etc. *)
val get : t -> id -> item
(* let's assume a persistent data structure ... *)
val add : t -> id -> item -> t
The problem that I come across is that of inserting a value. I could initialize the object in whatever way that I need. I could insert it into a structure with a given id. With the get
, I could get the same set of fields back. So far so good.
However, I desire a slightly different semantics. I would like to see the object being returned with get
to carry its own id. This means that the item being inserted should be somewhat different from the item received.
val get : t -> id -> item'
(* let's assume a persistent data structure ... *)
val add : t -> id -> item -> t
Disregarding variant tags, the difference between item'
and item
is that item'
would carry additional fields. At the type level that might actually be invisible, since the object types only carry about methods. At the level of class types, we have the extra fields reflected of course.
Now, the question is how do I form the item'
from item
in a generic fashion? I do not wish to manually code a family of classes with additional fields defined along the lines of
class record1' = object
inherit record1
val id : Id.t
end
class record2' = object
inherit record2
val id : Id.t
end
(* ... *)
How may I construct a new object with additional attributes from a given object?
A more general question is: how should I approach the problem of emerging “boilerplate” code in the context of this problem? Is code generation the only solution?
Alternative Approaches?
I would not mind to consider how to solve this problem without involving objects. I could use first-class modules as records. However, in practice, packing and unpacking seems to be rather unwieldy, and moreover it appears that explicit variants tagging is required for storing such records based on first-class modules in any collection. I also don’t see how my problem could be resolved — with modules in general the signatures need to be fully enumerated.