Enum in Elixir

At some step of development we may need Enum. But there are no enum in Elixir. Let’s try to make our implementation.

Metaprogramming is something that we should avoid. But that’s the way we will go :)

Imagine that we have already implemented Enum. Let’s see an example of their use. Define the Colors module:

defmodule Colors do
use Enum, [ # the enum implementation
green: 0,
yellow: 1,
red: 2
]
end

Use cases:

iex(1)> Colors.enum_list
[green: 0, yellow: 1, red: 2]
iex(2)> Colors.enum_keys
[:green, :yellow, :red]
iex(3)> Colors.enum_values
[0, 1, 2]
iex(4)> Colors.enum_key 2 
:red
iex(5)> Colors.enum_value :red
2
iex(6)> Colors.enum_value :magenta
nil
iex(7)> Colors.enum_key 42 
nil

or in changeset:

def changeset(struct, params \\ %{}) do
struct
|> cast(params, @required_fields ++ @optional_fields)
|> validate_required(@required_fields)
|> validate_inclusion(:color_id, Color.enum_values())
end

Buy the way, we can define not only integer based enums. For example:

defmodule Days do 
use Enum, [
Mon: “monday”,
Tue: “tuesday”,
Wed: “wednesday”,
Thu: “thursday”,
Fri: “friday”,
Sat: “saturday”,
Sun: “sunday”,
Weekend: [:Sat, :Sun]
]
end

I think that’s enough. Let’s see the details of Enum implementation.

defmodule Enum do
  defmacro __using__(which) when is_list(which) do
quote do
import unquote(__MODULE__)
     def enum_list do
unquote(which)
end
     def enum_value(key) do
case List.keyfind(unquote(which), key, 0) do
{_, value} -> value
nil ->nil
end
end
     def enum_values do
Keyword.values(unquote(which))
end

def enum_keys do
Keyword.keys(unquote(which))
end
     def enum_key(value) do
case Enum.find(unquote(which), nil,fn {k, v} -> v == value end) do
nil -> nil
{k, v} -> k
end
end
end
end
end

That’s all. You are free to use it.