DEV Community

Pankaj Sharma
Pankaj Sharma

Posted on

Ad-hoc Polymorphism in Elixir

Ad-hoc polymorphism is a kind of polymorphism that uses types and polymorphic functions (aka function overloading) to change the function behavior depending on the types of its arguments.

While behaviour and defprotocol are preferred options for achieving polymorphism in Elixir, there is also one more option, probably more simplistic. Ad-hoc polymorphism can be achieved in Elixir using a combination of plain structs, function overloading, pattern matching and destructuring.

First step is to wrap the state, that needs polymorphic behavior in a struct, as shown below.

  defmodule ConnectionHandler.FirstConnectionHandler do
    defstruct con: nil
  end

  defmodule ConnectionHandler.SecondConnectionHandler do
    defstruct con: nil
  end

Then create a module which will house the overloaded functions, that would pattern match over the structs that we just created.

  defmodule ConnectionHandler do
    alias ConnectionHandler.FirstConnectionHandler
    alias ConnectionHandler.SecondConnectionHandler

    def handle(%FirstConnectionHandler{con: con}) do
      "First"
    end

    def handle(%SecondConnectionHandler{con: con}) do
      "Second"
    end
  end

Below shown simple test case demonstrates how it all glues together.

defmodule ConnectionHandler.Test do
  use ExUnit.Case

  defmodule ConnectionHandler.FirstConnectionHandler do
    defstruct con: nil
  end

  defmodule ConnectionHandler.SecondConnectionHandler do
    defstruct con: nil
  end

  defmodule ConnectionHandler do
    alias ConnectionHandler.FirstConnectionHandler
    alias ConnectionHandler.SecondConnectionHandler

    def handle(%FirstConnectionHandler{con: con}) do
      "First"
    end

    def handle(%SecondConnectionHandler{con: con}) do
      "Second"
    end
  end

  test "ad-hoc polymorphism demonstration" do
    alias ConnectionHandler.FirstConnectionHandler
    alias ConnectionHandler.SecondConnectionHandler

    assert ConnectionHandler.handle(%FirstConnectionHandler{con: []}) == "First"
    assert ConnectionHandler.handle(%SecondConnectionHandler{con: []}) == "Second"
  end
end

While this is a way of achieving polymorphism in Elixir, one might want to consider more idiomatic approach of using defprotocol.

Update: Part 2 of this series demonstrates how using defprotocol retults in better encapsulation and fewer lines of code.

Top comments (0)