Image of Author
April 15, 2022 (last updated September 21, 2022)

Multimethods are a way to achieve polymorphism without objects. For example, when, in Elixir, you write

def noise('cat'), do: IO.puts('meow')
def noise('dog'), do: IO.puts('woof')

You are effectively using multimethods to achieve polymorphic behavior using arbitrary strings. In Elixir, you quickly intuit that you are writing "one" function, noise/1, but that one function has multiple clauses, which is the terminology Elixir uses to refer to them.

This baked-in feature of Elixir can also be achieved in other programming languages that don't natively support it, for example in Javascript you could manually implement it, naively, via a switch-case statement

const noise = (animal) => {
  switch (animal) {
    'cat' -> return 'meow'
    'dog' -> return 'woof'
    default -> return 'whereof one cannot speak, thereof one must remain silent'

This is a strictly inferior implementation because you lack the extensibility inherent in multi-clause constructions. If I want to add or remove a clause in Elixir, I just do. In the above Javascript, I edit an arbitrarily long switch-case statement. But, you can do much better. Instead of a switch-case statement, you can refactor towards a dispatch function that can store references to your various functions and invoke them reactively. Two examples I have found in the Javascript ecosystem are arrows/multimethod and multimethod.js.

The blog post that my colleague showed me that introduced me to multimethods as "a thing" (as opposed to "what Elixir does") is Polymorphism without objects via multimethods by Yehonathan Sharvit.