2011年9月21日水曜日

A Small Patch for Bizarre but User Controllable Limited Overloading


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

end
That's all. Simple. What I did here is just list the definitions of (+) for different types (int and float). Since one module cannot export more than one values with the same name, Those (+) are defined in separate modules. I named Int and Float but 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* Plus does? 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.Float
but in ADDITION, if open* finds more than one definitions with the same name, here (+), they are registered as overloaded. So, open* Plus overloads 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
end
You can just say open* SuperPlus to 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 + x is 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 -> int from its context, then the type of one could be fixed as int.:
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

The source is available from my μ-tated OCaml ranch, poormans_overloading branch: https://bitbucket.org/camlspotter/mutated_ocaml/src/f4aeda4f648a

0 件のコメント:

コメントを投稿