Multiple dispatch in lib was Re: Old comments about Java

Paulo Pinto pjmlp at progtools.org
Sun Apr 24 02:52:00 PDT 2011


Multiple dispatch is commonly used for fixing the "expression problem".
http://en.wikipedia.org/wiki/Expression_problem

This is usually easily done in functional languages, and relates to the best 
way to adapt
existing code to new uses, without changing the underlining code.

Multimethods provide a very good solution to the problem.

http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Dr-Ralf-Laemmel-Advanced-Functional-Programming-The-Expression-Problem
http://www.ibm.com/developerworks/java/library/j-clojure-protocols/?ca=drs-

--
Paulo

"Adam D. Ruppe" <destructionator at gmail.com> wrote in message 
news:iovuf1$2dhi$1 at digitalmars.com...
> bearophile wrote:
>> [multiple dispatch and what they use it for.]
>
> I wonder if we could do it in the library by using overloads and
> a generated dynamic cast.
>
> class Base {
>  // if the types are known, regular overloading does the job
>  void multipleDispatchFun(A a, A b) { writeln("Called (A, A)"); }
>  void multipleDispatchFun(A a, B b) { writeln("Called (A, B)"); }
>  void multipleDispatchFun(B a, B b) { writeln("Called (B, B)"); }
> }
>
> class A : Base {}
> class B : Base {}
>
> void main() {
>  Base base = new Base();
>  Base a = new A();
>  Base b = new B();
>
>  // doesn't compile - static types don't match
>  base.multipleDispatchFun(a, b);
>
>  // So, we'd have to cast it ourselves and try to call
>  if(dynamic casts are ok)
>     base.multipleDispatchFun(cast(A) a, cast(B) b); // would work
> }
>
>
> It's that last part that multiple dispatch solves (someone
> correct me if I'm wrong). It's a pain to do manually.
>
> But maybe D can do it automatically. We can generate another
> overload that takes the base class, tries to cast to all the
> existing overloads, and if not null, go ahead and call it.
>
> This isn't complete, but it works for this simple case so it's a
> start and proof of concept:
>
> =========
>
> import std.traits;
>
> template implementMultipleDispatch(alias Class, alias method) {
> //pragma(msg, multipleDispatchImpl!(Class, method,
> // __traits(identifier, method))());
>
>        // mixing it in right here didn't work for some reason
> alias multipleDispatchImpl!(Class, method,
> __traits(identifier, method)) implementMultipleDispatch;
>
> }
>
> // straight stringof hit a problem with forward reference, so this hack 
> will
> // do it
>
> string[] parameterTypeStrings(string methodString)() {
> string[] ret;
>
> int startingAt;
> int state = 0;
> foreach(idx, c; methodString) {
> switch(state) {
> // first, we find the opening param
> case 0:
> if(c == '(') {
> state++;
> startingAt = idx + 1;
> }
> break;
> case 1: // we're reading a type name
> if(c == ' ') {
> // just finished
> ret ~= methodString[startingAt .. idx];
> state = 2;
> }
> break;
> case 2: // now we're reading a name
> if(c == ' ') {
> state = 1; // another type
> startingAt = idx + 1;
> }
> if(c == ')') // we're done
> //break loop;
> state = 3;
> break;
> // we're done, but break loop doesn't work in CTFT
> case 3: // do nothing with the rest
> }
> }
>
> return ret;
> }
>
> string multipleDispatchImpl(alias Class, alias method, string 
> methodName)() {
> string code = `void ` ~ methodName ~ `(`;
>
> alias typeof(__traits(getOverloads, Class, methodName)) overloads;
>
> string baseType = Class.stringof;
> bool outputted = false;
>
> // we'll reuse the call string to make our calls
> string call = methodName ~ "(";
> char arg_char = 'a';
> foreach(arg; ParameterTypeTuple!(method)) {
> if(outputted) {
> code ~= ", ";
> call ~= ", ";
> } else
> outputted = true;
>
> code ~= baseType ~ " " ~ arg_char;
> call ~= " " ~ arg_char ~ "_casted";
> arg_char++;
> }
>
> call ~= ");";
> code ~= ") {";
>
> foreach(overload; overloads) {
>
> code ~= "{"; // we'll introduce a scope to do our casts
> string ifCheck; // this lists the checks for null
> outputted = false;
> arg_char = 'a';
>
> foreach(argument; parameterTypeStrings!(overload.stringof)) {
> // just try to cast all of them
> code ~= "auto ";
> code ~= arg_char ~ "_casted = cast(";
>
> code ~= argument;
>
> code ~= ") " ~ arg_char ~ ";";
>
> if(outputted)
> ifCheck ~= " && ";
> else
> outputted = true;
>
> ifCheck ~= arg_char ~ "_casted !is null";
>
> arg_char++;
> }
>
> // if none of the args are null, it's safe to do the call
> code ~= "if(" ~ ifCheck ~ ") {";
> code ~= call ~ "return;"; // return because we're done
> code ~= "}"; // end if
> code ~= "}"; // close our casting scope
> }
>
> code ~= "}";
>
> return code;
> }
>
> ==============
>
>
>
> Here's a test using it:
>
> import std.stdio;
>
> // copy paste the above in here
>
>
> class A : Base {}
> class B : Base {}
>
> class Base {
> void multipleDispatchFun(A a, A b) { writeln("Called (A, A)"); }
> void multipleDispatchFun(A a, B b) { writeln("CORRECT Called (A, B)"); }
> void multipleDispatchFun(B a, B b) { writeln("Called (B, B)"); }
>
> mixin (implementMultipleDispatch!(typeof(this), multipleDispatchFun));
> }
>
>
> void main() {
> Base base = new Base();
> Base a = new A();
> Base b = new B();
>
>        // calls the generated base class overload
> base.multipleDispatchFun(a, b);
>
>        // and, of course, if the static types are known, the
>        // regular overload works fine. only problem is if one
>        // is statically known and one is not. Then you shoud
>        // cast to the base type manually so it triggers the
>        // generated overload
> }
>
> ========
>
>
> This is a toy example, but if someone really wanted to spend the
> time, I think it proves it can be done for real examples too, at
> least in theory. 




More information about the Digitalmars-d mailing list