A Small Patch for Bizarre but User Controllable Limited Overloading
Yes, it is bizarre. Yes, it is limited. Yes, it is nothing new at all. But yes, it is simple and useful.
I have written a small patch to OCaml compiler to provide an SML style limited overloading. Limited means that you cannot derive overloading using overloaded values. For example, in SML
(+)is (limitedly) overloaded:
1 + 2; 1.2 + 3.4;But you cannot define overloaded double using
(* In SML syntax *) fun double x = x + x; (* It is not overloaded. *) (* Defaulted to int -> int *)The patch provides this "poorman's overloading" to OCaml, additionaly with controllability: you can choose what are overloaded. And this part is the bizarrest part of this patch.
Let's overload plus operators. First of all, list the overloaded instances:
module Plus = struct module Int = struct let (+) = (+) end module Float = struct let (+) = (+.) end endThat's all. Simple. What I did here is just list the definitions of
(+)for different types (
float). Since one module cannot export more than one values with the same name, Those
(+)are defined in separate modules. I named
Floatbut you can use whatever name you like. The preparation is done.
Now, make those
(+)overloaded. It is surprising simple:
open* Plus (* Strange open with a star *)Done. Now if you use
(+), it is overloaded!:
let _ = assert (1 + 2 = 3); assert (1.2 + 3.4 = 4.6);; (* It works! *)What
open* Plusdoes? It traverses its signature recursively and opens all the sub-modules. So in this case it is equivalent with:
open Plus open Plus.Int open Plus.Floatbut in ADDITION, if
open*finds more than one definitions with the same name, here
(+), they are registered as overloaded. So,
open* Plusoverloads the two
The overloading by
open*can be repeated and the overloaded instances can be easily accumulated. This provides simple and powerful "open" world overload control:
module Num = struct let (+) = Num.(+/) module Big_int = struct let (+) = Big_int.add_big_int end module Nat = struct let (+) = Nat.add_nat end module Ratio = struct let (+) = Ratio.add_ratio end end open* Plus (* overload (+) for int and float *) open* Num (* overload (+) for additional 4 num types! *) open* Int32 (* defines (+) for int32, omitted *) open* Int64 (* defines (+) for int64, trivial *) open* Natint (* defines (+) for natint *) (* Now you can use (+) for 9 num types! *)Or more simply, once you define the following:
module SuperPlus = struct include Plus let (+) = Num.(+/) module Big_int = struct let (+) = Big_int.add_big_int end module Nat = struct let (+) = Nat.add_nat end module Ratio = struct let (+) = Ratio.add_ratio end include Int32 include Int64 include Natint endYou can just say
open* SuperPlusto enjoy the overloading in your current module.
It is limited.
The overloading is limited. Any local ambiguity is reported as a type error immediately. For example,
let double x = x + xis rejected since it has no enough context type information to resolve the overloading of
(+). No defaulting, or fancy real polymorphic overloading.
One overloading must be locally resolvable by itself. The following example has no ambiguity, since
(+)is clear for
int -> int -> intfrom its context, then the type of
onecould be fixed as
module One = struct module Int = struct let one = 1 end module Float = struct let one = 1.0 end end open* Plus open* One let _ = assert (one + 2 = 3)But this overloading resolution propagation is not implemented for simplicity, and this example is rejected due to the false positive ambiguous use of
one. You need an explicit type annotation.
The source is available from my μ-tated OCaml ranch,