Let’s say I have some data type t
that always has a couple of fields and optionally has a few others. Something like this.
type t = {w: w; x: x; y: y option; z: z option}
Then you could imagine having functions to get the values of those fields.
val w : t -> w
val x : t -> x
val y : t -> y option
val z : t -> z option
I wanted to see if I could set it up so that the y
and z
functions could only be called on data that actually has something in y
and z
fields rather than None
. So I set up a GADT that takes records with only the present data, and then tracks whether y
and z
are present or not.
(* To track if y and z are present *)
type no = No
type yes = Yes
type w = W
type x = X
type y = Y
type z = Z
type ('has_y, 'has_z) t =
| Wx : {w: w; x: x} -> (no, no) t
| Wxy : {w: w; x: x; y: y} -> (yes, no) t
| Wxz : {w: w; x: x; z: z} -> (no, yes) t
| Wxyz : {w: w; x: x; y: y; z: z} -> (yes, yes) t
let w : type a b. (a, b) t -> w = function
| Wx v -> v.w | Wxy v -> v.w | Wxz v -> v.w | Wxyz v -> v.w
let x : type a b. (a, b) t -> x = function
| Wx v -> v.x | Wxy v -> v.x | Wxz v -> v.x | Wxyz v -> v.x
let y : type a. (yes, a) t -> y = function
| Wxy v -> v.y | Wxyz v -> v.y
let z : type a. (a, yes) t -> z = function
| Wxz v -> v.z | Wxyz v -> v.z
And that compiles and works okay…the compiler doesn’t let you call y
and z
on variants that don’t have that data present. But, if instead of only two optional fields there were more, this way could get unwieldy pretty quickly. So I was wondering if there was a better way to encode this.
Some other ideas I had:
- Don’t bother trying to “connect” the different variants in a GADT, just make them all separate types with specific functions for those types.
- Use objects or classes and take advantage of row polymorphism or subtyping/mixins (no idea how that would look as I’ve never used the “O” in OCaml)
The xy problem is basically this: I have some external service that returns data that is more or less the same in most of the fields and differs in a few others. Eg, A, A', A'', ...
, with each of those data types sharing many of their fields, and other sets B, B', B'', ...
sharing most of their fields, and so on. And I would like to deal with them in a nice way.