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.