I am wondering if it is possible to build something similar to multiple dispatch in OCaml. To do that, I tried to make an explicit type for the input signature of a multimethod. As an example, I define a number type
type _ num =
| I : int -> int num
| F : float -> float num
Now I would like a function add
to sum an 'a num
and a 'b num
and return an int num
if both 'a
and 'b
are int
, and a float num
if at least one of them is a float
. Also, the type system should know which constructor the output will use. I.e. it should be statically known at the function call that the output is of type int num
for example.
Is that possible? So far I can only manage a function of signature type a b. a num * b num -> a num
for example, so that the (more general) float would always have to be supplied as the first argument. The case int num * float num
would have to be disallowed, leading to a non-exhaustive pattern match and runtime exceptions.
It seems that one would need a signature like type a b. a num * b num -> c(a,b) num
where c
is a type function which contains the type promotion rules. I don't think OCaml has this. Would open types or objects be able to capture this? I'm not looking for the most general function between types, it's enough if I can list a handful of input type combinations and the corresponding output type explicitly.
The specific case you are asking about can be solved nicely using GADTs and polymorphic variants. See calls to
M.add
at the bottom of this code:That prints
You cannot change any of the above
to_float
s toto_int
: it is statically known that only adding twoI
s results in anI
. However, you can change theto_int
toto_float
(and adjust theprintf
). These operations readily compose and propagate the type information.The foolery with the nested
match
expression is a hack I will ask on the mailing list about. I've never seen this done before.General type functions
AFAIK the only way to evaluate a general type function in current OCaml requires the user to provide a witness, i.e. some extra type and value information. This can be done in many ways, such as wrapping the arguments in extra constructors (see answer by @mookid), using first-class modules (also discussed in next section), providing a small list of abstract values to choose from (which implement the real operation, and the wrapper dispatches to those values). The example below uses a second GADT to encode a finite relation:
Here, the type function is
('a, 'b, 'c) promotion
, where'a
,'b
are arguments, and'c
is the result. Unfortunately, you have to passadd
an instance ofpromotion
for'c
to be ground, i.e. something like this won't (AFAIK) work:Despite the fact that
'c
is completely determined by'a
and'b
, due to the GADT; the compiler still sees that as basically justWitnesses don't really buy you much over just having four functions, except that the set of operations (
add
,multiply
, etc.), and the argument/result type combinations, can been made mostly orthogonal to each other; the typing can be nicer and things can be slightly easier to use and implement.EDIT It's actually possible to drop the
I
andF
constructors, i.e.This makes the usage much simpler:
However, in both cases, this is not as composable as the GADT+polymorphic variants solution, since the witness is never inferred.
Future OCaml: modular implicits
If your witness is a first-class module, the compiler can choose it for you automatically with modular implicits. You can try this code in the
4.02.1+modular-implicits-ber
switch. The first example just wraps the GADT witnesses from the previous example in modules, to get the compiler to choose them for you:With modular implicits, you can also simply add untagged floats and ints. This example corresponds to dispatching to a function "witness":