Avoiding extra matching with extensible GADTs

What is the run-time cost of using a record here instead of module or an object? Would a module, or a first-class module be more efficient (assume compilation with flambda and -O3)?

I agree that the method you are suggesting is a good approach, and it would work every time where a generic operation on a single type is required.

The problem that appears in practice is related to the need to form property lists from relatively arbitrary subsets of types in a program. In case of a game, we may have battlescape weapons, aircrafts, munitions, loot, dumb items, artefacts, etc… Each may employ some of the game data types, but not others. The corresponding property lists — lists that contain tagged items, like e.g. [(`Ignores_pain , Bool true) ; (`Sprite , Sprite <abstract>); (* ... *)] — thus must be typed somewhat differently (sometimes we may need sprites, sometimes not, sometimes we need sound, …, and sometimes we need sprites with different handling, … etc.). In fact, with tags on the left, we actually need to distinguish each case: for some weapons we have combination power, range, for others we have power, powerDecay, etc… In general, we need arbitrary subsets of tags expressible in a type.

What is the best approach here? To attempt techniques like those described in this thread (notably e.g. here)? Note that even if we found the best type to express sets and subsets of properties — corresponding to possible tagged item values, — we are still facing a dilemma that the multiple tagged item types should be represented by a single type value in order to be usefully processed. Do you see how this case is different from the application of type-specific operations on a single type?

What we end up here once again is a case of a scheme with a global data tags type with phantom row types (whether objects or polymorphic variants) used for sub-setting the admissible tags.

The problem with this approach is that we are once again facing the need to extend the global type whenever we introduce a new game data type. For example, I may introduce an animation. Doing so would require me to manually extend the global type that represents the sum of all tags with a tag representing an animation.

The only solution that I see here is to generate textually the centralized type representation from the local definition of cases and operations on them. An alternative may be to use OO features and classes.

I wonder, if we need to pay the cost of polymorphic variants every time we need to put items in a collection?

With new operations, I think that I’ll need to change the interface that enumerates all the operations regardless. In that case, all that remains is to edit all instances to implement additional operations.

I think that extending the types themselves is a more difficult case here, particularly when we need to define operations on tagged variants, in cases where tagging is genuinely required. The solution you have suggested above works by eliding tagging altogether. However, we must ask a question:

:question: Could tagging be always elided?

While the seeming obvious answer appears to be «No», an interesting thought emerges. What if the cases where tags need to be added in a mechanical manner are only the simple cases, when we just need to enumerate types? Regardless, I still don’t see how we could generically operate on collections of properties without having those properties unified into a single type by tagging, and then put into some collection (e.g. a simple list).

I suppose, in the foreground I might have an interface that puts the items into an opaque type of my own in front of a generic collection. Then, I could have my own set of tags local to the internals of that opaque type. Something along the lines of:

  type t
  val add_attr : t -> (Tag.t , 'a) -> t

And now, the internal representation of types used as input to internal converters to external property list data might as well just rely on the internally-specified set of tags, and basic subsetting with phantom types…

Hmm… and we could actually have the attributes type to carry an extra phantom type to denote what set of attributes it carries.

The only interesting question remaining in this case is how may we automatically generate the desired local variant type representing internally the property list?