Derive Them Protocols

January 6, 2019

Using derive to implement a protocol is a quick way to give functionality to your modules.

Let’s say you had a protocol that rounds values for you. Like such:

defprotocol Math do
  def round(v, p)

defimpl Math, for: Float do
  defdelegate round(v, p), to: Float

Which allows us to do something like this:

iex(1)> Math.round(1.556, 2)  

Now say we wanted to round all the float values in our custom data structures. Instead of having to implement the logic over and over would be nice if we could do it simply once.

Here is an example data structure.

defmodule Data do
  defstruct x: 1.556, y: 2.556

If we tried to use our protocol as it stands now on the Data structure we’d get:

iex(1)> Math.round(%Data{}, 2)
** (Protocol.UndefinedError) protocol Math not implemented for %Data{x: 1.556, y: 2.556}. This protocol is implemented for: Float
    (super_happy_fun_land) lib/math.ex:1: Math.impl_for!/1
    (super_happy_fun_land) lib/math.ex:2: Math.round/2

Let’s implement the Math protocol for any such data structure in our system to take advantage of.

defimpl Math, for: Any do
  defmacro __deriving__(module, _struct, _opts) do
    quote do
      defimpl Math, for: unquote(module) do
        def round(data, p) do
          rounded_map =
            |> Map.from_struct()
            |> Enum.reduce(%{}, fn {k, v}, acc ->
              Map.merge(acc, %{k => round_float(v, p)})

          struct(unquote(module), rounded_map)

        defp round_float(v, p) when is_float(v), do: Math.round(v, p)
        defp round_float(v, _), do: v

We’ve implemented the Math protocol for Any and then defined the deriving macro which will allow us to use this in our modules.

Then we can implement the round function for any module that takes advantage of the derive.

Finally let’s add this to our Data module and give it a try.

defmodule Data do
  @derive [Math]
  defstruct x: 1.556, y: 2.556

iex(1)> Math.round(%Data{}, 2)
%Data{x: 1.56, y: 2.56}

As you can see using derive is a nice tool if you have lots of data structures that all need to implement a protocol in a simple fashion and saves you the time from having to repeat yourself over and over.

Contact Me -> Click!