On DRY Spells, Pt. 2

In this sporadic series whose continuation is contingent on how I’m feeling on any given day/week/month, I pick some well known OO design patterns and see if they’re any use in a dynamic and/or functional language. Last time we saw how the template method pattern almost evaporates when you use a language with lexical closures. This time let’s explore the phenomenon of multiple dispatch.

Alright, what is it?

Cool, I’ve lured you in, this is pure linkbait isn’t it?! :) Let’s say you’re coming from Java land, specifically a place where you have an interface like this:

public interface Widget {
    void doSomething();
}

Let’s also say you’ve got two concrete implementations:

public class WidgetA implements Widget {
    @Override
    public void doSomething() {
        System.out.println("WidgetA");
    }
}

public class WidgetB implements Widget {
    @Override
    public void doSomething() {
        System.out.println("WidgetB");
    }
}

All very taxing stuff, it must be said. What if you want to make a call to doSomething() without caring about the receiver type? That’s fine; it’s a normal use of type polymorphism in Java and the compiler will happily figure out the correct type to dispatch the method call onto. For example:

public class WidgetConsumer {
    
    public void iDunno(Widget widget) {
        widget.doSomething();
    }
}

Whatever Widget implementation you pass in, the call site can be resolved at compile time. Let’s step it up a notch: what if we want to hit a different version of iDunno() depending on Widget subtype?

public class WidgetConsumer {
    
    public void iDunno(WidgetA widget) {
        System.out.println("WidgetA logic");
    }

    public void iDunno(WidgetB widget) {
        System.out.println("WidgetB logic");
    }
}

Trying to call iDunno() now will make the compiler explode- it doesn’t know which overloaded version to use. The normal workaround for this secondary dispatch is to use the visitor pattern. Here, the two lumps of iDunno logic in WidgetConsumer would suffice as a visitor, while the concrete Widget implementations are the things that get visited. To top the example off we need some kind of client to kick off the visitation, and we need a new method on widget to allow the WidgetConsumer to visit each implementation:


public interface Widget {
    void doSomething();
    void accept(WidgetConsumer consumer);
}

public class WidgetA implements Widget {
    @Override
    public void doSomething() {
        System.out.println("WidgetA");
    }

    @Override
    public void accept(WidgetConsumer consumer) {
        consumer.iDunno(this);
    }
}

public class WidgetB implements Widget {
    @Override
    public void doSomething() {
        System.out.println("WidgetB");
    }

    @Override
    public void accept(WidgetConsumer consumer) {
        consumer.iDunno(this);
    }
}

public class WidgetClient {

    public static void main(String[] args) {
        WidgetConsumer consumer = new WidgetConsumer();
        WidgetA widget = new WidgetA();

        widget.accept(consumer); // should print "WidgetA logic"
    }

}

That’s a common GoF workaround, and for my money I think it’s pretty clever (although hampered by the lack of expressiveness afforded by a statically typed language). What if we had a language which relaxed the need to resolve the concrete type ahead of time? They’re actually not ten-a-penny. We have the same problem in JavaScript:

function iDunno(hopefullyAString) {
    // do something with a string
}

function iDunno(hopefullyANumber) {
    // do something with a number
}

That code doesn’t even make sense to read; clearly, dynamic and/or weak typing on its own is not sufficient. Are we stuck with visitors forever then?

Just put up with Java already

Nooooo! There are a few of languages which can resolve this issue and spring to mind, but I’m going to revert to character and bang the drum for Clojure. Go figure. You’ve committed to reading this waffling post already so you get what you pay for. And Clojure is great anyway. This one’s actually a bit of a misnomer- despite targeting the JVM, the JVM actually only supports single polymorphic dispatch through a classic vtable or similar lookup (although most modern JVMs can elide the lookup if they know a call site is monomorphic- that’s a speculative optimisation).

Anyway, WTF does a Clojure multimethod look like? Well, it looks like this good Sir / Madam:

; define the signature w/ dispatch function
(defmulti iDunno class)

; handler for WidgetA
(defmethod com.example.WidgetA [_] (println "WidgetA"))

; handler for WidgetB
(defmethod com.example.WidgetB [_] (println "WidgetB"))

…where class is a function that gets called before the actual method + args are invoked. Awkwardly, that single example pretty much wraps up the solution; if you have a language with constructs for runtime method dispatch, what we have is another pattern which almost evaporates.

This isn’t hating on Java or any other statically typed language- but it’s a cautionary tale that getting stuck in a particular programming paradigm puts you at risk of falling prey to the Law of the instrument, and at the risk of much trolling, that’s something that is particularly prevalent in industrial OOP circles: Java/C#/C++, I’m looking at you.

Leave a Reply

Your email address will not be published. Required fields are marked *