Basic but useful example of Elixir Protocols

August 11, 2019

Assuming you know the theory, in this example we’ll provide one login interface for different type of users.

mix new protdemo --sup

Structs

This example shows that passing different data types to our login function will trigger a different implementation.

Create a new admin directory under lib/protdemo and put there a new file admin.ex:

# lib/protdemo/admin/admin.ex

defmodule Protdemo.Admin do
  defstruct [:admin_name, :password]

  def login(who), do: {:ok, who.admin_name <> " logged in as Admin"}
end
# lib/protdemo/user/user.ex

defmodule Protdemo.User do
  defstruct [:user_name, :password]

  def login(who), do: {:ok, who.user_name <> " logged in as User"}
end

Protocols

The most important part now. Create a new file auth.ex under lib/protdemo

# lib/protdemo/auth.ex

defprotocol Protdemo.Auth do
  def login(creds)
end

defimpl Protdemo.Auth, for: Protdemo.Admin do
  def login(admin_creds), do: Protdemo.Admin.login(admin_creds)
end

defimpl Protdemo.Auth, for: Protdemo.User do
  def login(user_creds), do: Protdemo.User.login(user_creds)
end
# lib/protdemo.ex

defmodule Protdemo do
  alias Protdemo.Auth

  def login(creds), do: Auth.login(creds)
end

Testing

# test/protdemo_test.exs

defmodule ProtdemoTest do
  use ExUnit.Case
  doctest Protdemo

  alias Protdemo.Admin
  alias Protdemo.Auth
  alias Protdemo.User

  test "Log in as User" do
    {:ok, "rafal logged in as User"} = Auth.login(%User{user_name: "rafal", password: "test"})
  end

  test "Log in as Admin" do
    {:ok, "rafal logged in as Admin"} = Auth.login(%Admin{admin_name: "rafal", password: "test"})
  end
end