DEV Community

Pankaj Sharma
Pankaj Sharma

Posted on

Ad-hoc Polymorphism In Elixir Using defprotocol

This is a follow up on my previous post on Ad-hoc Polymorphism. I highly recommend reading the previous post first. In this post we will refactor the same code to use defprotocol construct. At the end of this post you will notice that using defprotocol results in better encapsulation and fewer lines of code.

defprotocol is a means to achieving ad-hoc polymorphism. Ad-hoc polymorphism in simple terms is a technique of varying behaviour based on their types. Complex states can be modeled in Elixir using the defstruct construct. States defined using defstruct can be used as types for achieving ad-hoc polymorphism.

Lets use defprotocol to define a protocol (aka behavior) that we want to vary based on structs.

  defprotocol ConnectionHandler do
    def handle(con)
  end

defimpl let's us implement different behaviors for protocols defined using the defprotocol construct, as shown below.

  defmodule ConnectionHandler.FirstConnectionHandler do
    defstruct con: nil

    defimpl ConnectionHandler, for: ConnectionHandler.FirstConnectionHandler  do
      def handle(%ConnectionHandler.FirstConnectionHandler{con: con}) do
        "First"
      end
    end
  end

  defmodule ConnectionHandler.SecondConnectionHandler do
    defstruct con: nil

    defimpl ConnectionHandler, for: ConnectionHandler.SecondConnectionHandler  do
      def handle(%ConnectionHandler.SecondConnectionHandler{con: con}) do
        "Second"
      end
    end
  end

Below shown test demonstrates how everything glues together. If you have read the previous post, you might notice that no change has been made to the test.

  defmodule Blaze.Test do
  use ExUnit.Case

  defprotocol ConnectionHandler do
    def handle(con)
  end

  defmodule ConnectionHandler.FirstConnectionHandler do
    defstruct con: nil

    defimpl ConnectionHandler, for: ConnectionHandler.FirstConnectionHandler  do
      def handle(%ConnectionHandler.FirstConnectionHandler{con: con}) do
        "First"
      end
    end
  end

  defmodule ConnectionHandler.SecondConnectionHandler do
    defstruct con: nil

    defimpl ConnectionHandler, for: ConnectionHandler.SecondConnectionHandler  do
      def handle(%ConnectionHandler.SecondConnectionHandler{con: con}) do
        "Second"
      end
    end
  end

  test "demonstration of `defprotocol`" do
    alias ConnectionHandler.FirstConnectionHandler
    alias ConnectionHandler.SecondConnectionHandler

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

This post demonstrates that in Elixir using defprotocol for adhoc-polymorphism leads to fewer lines of code and better encapsulation.

Top comments (0)