Hello! Suppose I have the following GADT in Ocaml:
type _ t =
| A : f t
| B : f t * f t -> f t
| C : c t * c t -> f t
| D : [< f | c] t * c t -> c t
| E : c t
| T : string -> [< f | c] t
where f
and c
are polymorphic variants containing a single constructor:
type f = [ `f ]
type c = [ `c ]
This allows me to combine these constructors in very specific ways. For example, constructor B
does not allow any constructor that does not have type f t
, while the first parameter of constructor D
may accept any constructor of type c t
OR f t
. Next are a few examples of terms that are achievable using this type
A
B (A, B (A, A))
E
T "test"
D (A, E)
D (T "hello", T "world")
Now, I wish to write a mapping function that essentially “removes” the constructor T
out of a term of either type f t
or c t
. When the constructor T
is encountered, it should lookup for a value (which can be of either type f t
or type c t
) on a mapping environment (which right now we can assume to be a list of associations from string to any constructor of the aforementioned type). Now, as far as I have tested, this seems to be somewhat impossible (or at least very tedious to write) for two main reasons:
-
inconsistency from constructor D: if we have the term
D (T "hello", E)
,T "hello"
could yield a value of either typef t
orc t
, depending on what the binding for “hello” present in the environment is; -
type signatures: even if we eliminate constructor
D
, a termT "hello"
may still yield different results depending on what the environment contains. At the end of the day, constructorT
has a type[< f c] t
while the result should be eitherf t
orc t
It is utterly possible that I’m overcomplicating this problem, and its solution could as well be trivial whilst being completely blind to it, and its nearly certain I’ve delved so deep into this issue I no longer understand the mechanisms behind the solutions I’ve attempted. However, I found this to be an enticing puzzle. Thus, I have spent a fair amount of time developing some solutions, all of which have failed me in some way or another:
-
Separating the
f t
andc t
types into their own separate types: However, constructorT
should be present in both types, and because they would have identical functionality during the mapping process, I do not wish to add two distinct constructors for this instance. Furthermore, this solution is contingent on having two different mapping functions (one for each type), which is something I’m trying to avoid (this was actually the initial state of this system/mess and the main reason why I tried to work with GADTs and polymorphic variants in the first place); -
Replacing the GADT for polymorphic variants: This would allow some sort of subtyping - if we have two polymorphic variant types
fpv
andcpv
for their corresponding “base types”, I could make my mapping function work on these polymorphic variants in addition to the constructorT
, while returning only the polymorphic variants (something akin to having the signaturef : env -> [< fpv | cpv | `T of string] -> [< fpv | cpv]
). However, I have constructors that would depend on these polymorphic variants (i.e, at least one of its arguments will require an argument [< fpv | cpv]), which seems to be impossible with the typechecker in hand; -
Keep the GADT, but having separate mapping functions for
f t
andc t
: However, this would not work for constructorD
(since we would not know which function to call (and no, I cannot add another parameter to this constructor that would explicitly tell which function we should invoke - i already tested that as well)), nor would it work for a termT "hello"
for the same exact reason; -
Mapping from this GADT onto a new one without the constructor
T
: It works for most constructors (assuming we make use of type signatures, which I do not have a problem with), however it does not work for theT
constructor, since the resulting GADT does not contain any constructor with type[< f | c] t
I feel like I’m running out of options to work with, and I don’t know what to do next. What else can I try? If this is indeed a trivial problem but I’m simply lacking vision, please do provide me with some resources for me to read and to gain some inspiration. At this point, I’m willing to try (almost) anything.