Background: I was trying to create something as described in the paper, Data Type à la Carte - but trying to see if OCaml's polymorphic variants could lead to a clean ReasonML implementation.
My code here is in ReasonML syntax, but the question is equally applicable to OCaml.
I start by defining two modules for a Val
and a Add
, both implementing an fmap
- making them a haskel-style functor.
module type Functor = {
type t('a);
let fmap: ('a => 'b, t('a)) => t('b);
};
module Val = {
type t('e) = [ | `Val(int)];
let fmap = _ =>
fun
| `Val(x) => `Val(x);
};
module Add = {
type t('e) = [ | `Add('e, 'e) ];
let fmap = f =>
fun
| `Add(x, y) => `Add((f(x), f(y)))
};
I can fairly easily create an Algebra
data type that combines these two modules into one, with a really simple fmap
implementation.
module Algebra = {
type t('t) = [ Val.t('t) | Add.t('t)];
let fmap = (f, x) =>
switch (x) {
| #Val.t as v => Val.fmap(f, v)
| #Add.t as o => Add.fmap(f, o)
};
};
This compiles and works in a larger context where I can evaluate an expression consisting of both Val
and Add
values.
However, as a programmer who would like to avoid writing boiler-plate code, my next step would be to create a functor (OCaml functor) that can generate such a module from any two compatible modules.
My first attempt is this:
module JoinAlgebra = (A1: Functor, A2: Functor) => {
type t('t) = [ A1.t('t) | A2.t('t)];
let fmap = (f, x) =>
switch (x) {
| #A1.t as v => Val.fmap(f, v)
| #A2.t as o => Add.fmap(f, o)
};
};
But this doesn't work. As the A1.t
and A2.t
can be anything, I cannot combine them as a polymorphic variant.
Error: The type A1.t('t) is not a polymorphic variant type
I tried adding a type constraint to the Functor
module type:
module type Functor = {
type t('a) = 'a constraint [> ] = 'a;
let fmap: ('a => 'b, t('a)) => t('b);
};
module JoinAlgebra = (A1: Functor, A2: Functor) => {
type t('t) = [ A1.t('t) | A2.t('t)]; // This line fails
}
Now I get the compiler error
Error: The type A1.t([> ]) is not a polymorphic variant type
Is there any way I can create a module-functor that automatically based on the two modules?
A note about OCaml versions: I am using bucklescript v. 5 here, which uses the OCaml 4.02 compiler. But solutions that require 4.06 are also welcome (support should be coming to Bucklescript)