

Show HN: multimethod.js - a Clojure-inspired multimethod library for JavaScript - KrisJordan
http://krisjordan.com/multimethod-js

======
brian_c
Neat! I made something (I think) similar a while back, also inspired by
Clojure. Mine's more of a proof-of-concept, as I have zero Clojure experience
outside some very casual reading.

<https://github.com/brian-c/patterned-function>

------
hyperturtle
I like this very much. It's like a lightweight dispatcher/router/alternative
to tons of switches/if & elses. And also that the source is very short and
readable.

------
cfontes
Nice stuff, I liked the approach.

But what is the benchmark for this kind of structure against regular JS
workflow? I guess it will be slower, but how much ?

~~~
KrisJordan
Good question.

Performance will depend on a number of factors, such as how complex your
dispatch function is, how complex your dispatched values and match values are
(deep equality becomes more expensive as the compared objects grow in
complexity), and how many methods are registered. In general, it is much more
expensive than native switch statements or prototype chain lookups.

If performance is of utmost importance and you can substitute a switch
statement or prototype hierarchy for a multimethod, you should. Multimethods
are a useful pattern for achieving things that either aren't possible in
switch (add/remove cases dynamically, deep equality case matching) or are not
naturally/painlessly expressed with a prototype hierarchy.

~~~
swannodette
It doesn't have to be any slower than highly optimized switch statements if
you're willing to compile to JavaScript. All you need analysis / compilation
support. I've been working on this for a while now for Clojure /
ClojureScript.

------
gerggerg
in coffeescript:

    
    
        fib = (n) -> switch n
          when 0 then 0
          when 1 then 1
          else fib(n-1) + fib(n-2)
    
        alert fib 20

~~~
KrisJordan
While fib is a good example of concisely illustrating the control flow of a
multimethod, it is a bad example of where it makes sense to use a multimethod.
What is interesting about a multimethod is you can change your dispatch logic
and bind new cases well after your multimethod function is constructed. In
situations like fib where you know every case you need to handle up front you
should certainly use 'switch'.

To illustrate this flexibility in the context of fib, multimethod.js, and
CoffeeScript:

    
    
       > fib = multimethod()
               .when(0, 0)
               .when(1, 1)
               .default (n) -> fib(n)
       > fib 20
       6765
    
       > fib.dispatch (n) -> n.numberVal or n
       > fib 20
       6765
    
       > fib { numberVal: 20 }
       6765
    
       > fib.when 2, 1
       > fib.when 3, 2
       > fib 20
       6765 (now "optimized" with fewer recursive calls than before)

~~~
gerggerg
Could you comment on a good use-case for this? A time when it would actually
be better to construct a switch block dynamically vs all at once.

~~~
KrisJordan
Great question, probably worthy of its own post. Rather than trying to think
of times you want to construct switch statements dynamically, try to think of
times you'd like to specialize how a generic function call will respond to
arguments it may not even know about.

In object-oriented programming, you can define a base class "Animal" with a
method "makeNoise" and subclasses with a method "makeNoise" with that will
return "bark" if you call a Dog's makeNoise or "meow" if you call a Cat's.
When you first defined the Animal's "makeNoise" method you didn't need to know
how anything about the subclasses that would eventually implement it. Your
algorithms could depend on "makeNoise" returning the noise specific to the
animal in question.

Multimethods are a functional approach to polymorphism without a
class/prototype hierarchy. The second example in the post shows how you might
implement a multimethod that calculates the area of shapes. Perhaps a separate
module introduces new kinds of shapes (like new kinds of subclasses in OO).
These shapes can register their area implementations with the 'area'
multimethod. Other algorithms that use the 'area' multimethod will now be able
to operate on these new kinds of shapes. Multimethods provide a means to
polymorphism while keeping data and functionality decomposed.

Like swannodette says more beautifully and concisely, though, it's an even
more generic/flexible dispatch than thinking in terms of OO polymorphism.

