Reputation: 34884
I want to create a enum in Elixir. Is it possible? By enum I mean enums from C/C++ or Ruby or in many other languages. I'm aware of the Enum
module but that's just a module -- a container for functions basically.
Upvotes: 16
Views: 11787
Reputation: 21
I dont know if it is a good idea, but you can create a module (CustomEnum) where you define functions as Enums.
defmodule CustomEnum do
def monday, do: 0
def tuesday,do: 1
end
Then, you can call CustomEnum.monday and get value 0
Write all this functions could be boring, so you can create a list of tuples and dynamically create your function for each item. EX>
defmodule CustomWeekDaysEnum do
weekdays = [
monday: {0},
tuesday: {1},
wednesday: {2},
thursday: {3}
....
]
#creating functions dynamically
for{function_name, {value} <- weekdays do
def unquote(function_name)(), do: unquote(value)
end
end
Now you call CustomWeekDaysEnum.wednesday and get 2, or CustomWeekDaysEnum.thursday and get 3.
And now, if you need a new Enum item, just put it in weekdays List and your function will be created at compile time.
weekdays is a list of tuples, so your enums can have multiple values inside. you just have to change unquoted def to return the tuple or just specific values
Just remember: Functions dynamically created are cool but your code may become harder to read.
You can find more about it in Saša Jurić post
Upvotes: 2
Reputation: 1245
You could use for example this library: jcomellas/ex_const. It allows you to define enums like this:
enum <name> do
<key_1> <value_1>
<key_2> <value_2>
[...]
end
Sample code (taken from documentation on GitHub):
Definition:
enum country_code do
argentina "AR"
italy "IT"
usa "US"
end
Usage:
require Settings
import Settings
value = "AR"
case value do
country_code(:argentina) ->
{:ok, "Argentina"}
country_code(:italy) ->
{:ok, "Italy"}
code when code == country_code(:usa) ->
{:ok, "United States"}
_ ->
{:error, {:must_be_one_of, country_codes()}}
end
Upvotes: 1
Reputation: 146
If I understand you correctly, you are looking for a way to use enumeration in Elixir. You can do this with an instance of a Map using :atoms as keys.
weekdays = [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday]
|> Enum.with_index
|> Map.new
Or a bit more manually
weekdays = %{
monday: 0,
tuesday: 1,
wednesday: 2,
thursday: 3,
friday: 4,
saturday: 5,
sunday: 6}
If you're using :atoms as keys, the short-hand access syntax is:
iex> weekdays.monday
0
iex> weekdays.friday
4
But usually using atoms such as :error or :ok doesn't require you to do these declarations.
Upvotes: 13
Reputation: 9089
I think the short answer is No. If by enum, you mean a static user defined type enforced at compile time, (i.e. c enums), then the answer is definitely no.
The long answer is that there are only a fixed number of basic data types in the underlying VM of Elixir/Erlang. There are the basic data types:
[Atom, Integer, Float, BitString, Regexp, PID, Function, Reference, Port]
and the 3 Container types.
[Tuple, List, Map]
You can compose these types to create very complex data structures, but if you want to apply "rules" to the behaviour of those composed types, you need to create a library or module of functions for manipulating those types according to the rules.
Since Elixir is a dynamic language, it's is always possible to "reach" into any complex data type and completely transform it. There's no way to enforce rules on values unless you do that through a set of defined functions. My guess is that what you're looking for is a FixedSet
type than can only take values from a pre-defined list of values. That would be relatively straightforward to implement as a module, but writing a module of functions( and likely some macros) is the only way to accomplish that kind of functionality in Elixir.
Upvotes: 3
Reputation: 54674
You could just use atoms. For static analysis, you can define a type that accepts only certain values and use that in your typespecs. You can then check your code with dialyxir for example.
@type state :: :a | :b | :c
@spec set_state(pid, state) :: :ok | {:error, term}
def set_state(pid, state)
# ...
end
@spec get_state(pid) :: state
def get_state(pid)
# ...
end
Upvotes: 15