ImNotAVirus

ImNotAVirus

SimpleEnum - Use Enumerations in Elixir

Hi everyone,

I have just published the first version of a library that I have been using in my projects for over a year now: SimpleEnum - a simple library that implements Enumerations in Elixir.

SimpleEnum was designed to be :

  • fast: being based on a macro system, access to the Enum will be resolved at compile time when it is possible
  • simple: the use of the library has been designed to be as simple as possible for a developer to use. In addition to providing Enums, it automatically defines their types and provides an introspection system.

Examples of uses can be found in the documentation : SimpleEnum — simple_enum v0.1.0

Proposals :

To improve SimpleEnum, I had several ideas like

  • Add the defenump function in order to create private Enums: currently, all Enums are public
  • Add an EnumValueError exception for developers who need rescue. Currently, ArgumentError is used
  • Add a decorator @allow_duplicate_values true or @unique false to allow duplicates values in Enum
  • Add macros is_enum/1, is_enum_key/1 and is_enum_value/1 (eg. is_color(:red), is_color_key(:red) and is_color_value(:red)) for each Enum. An example of implementation can be found here.

What do you think? Would you need a feature in the list or another one?

Any issues, suggestions or contributions are welcome.
Cheers


Links:

Most Liked

ImNotAVirus

ImNotAVirus

Thanks for the question.

I’ve never used ex_const so my answer probably won’t be complete but here are the main differences I noticed.

First of all, you must know that when writing SimpleEnum, I got a lot of inspiration from how Enumerations work in C++. This explains some of my design choices like the fact that it is impossible to have 2 times the same key in an Enum.

Difference 1: with ex_const, an Enum can have duplicates keys and values.
This is a valid code :

enum type do
    key1 	0
    key1	1
    key1	1
end

iex> Enums.type(:key1)
0

Here SimpleEnum will raise an exception at compile time:

defenum :type, [key1: 0, key1: 1, key1: 1]
# ** (CompileError) tests.exs:13: duplicate key :key1 found in enum Enums.type

Difference 2: ex_const does not support lists of keywords for creating Enums
With SimpleEnum, you can do

defenum :type, [:key1, :key2, :key3]

iex> type(:__enumerators__)
[key1: 0, key2: 1, key3: 2]

Difference 3: ex_const can be used at compile time to get a value from a key but not the opposite.

enum type do
    key1 	0
end

# Valid: when x == 0
def test(x) when x == Enums.type(:key1), do: x
# Raise
def test(x) when x == Enums.type(0), do: x
# Raise
def test(x) when x == Enums.from_type(0), do: x

With SimpleEnum you can do

defenum :type, [:key1]

# Valid: when x == 0
def test(x) when x == Enums.type(:key1), do: x
# Also valid: when x == :key1
def test(x) when x == Enums.type(0), do: x

Difference 4: ex_const does not seem to have any introspection helper.

ImNotAVirus

ImNotAVirus

Thanks for your interest!

Short answer:
The two main differences are that SimpleEnum does not depend on Ecto (it has no dependencies) and it adds Enums at compile time, not just at run time.

Long answer:
As mentioned in its documentation on reflection, Ecto.Enum's introspection is only at runtime.

I originally wrote SimpleEnum because I needed to be able to work with Enumeration at compile time (in guards for example).

For example, let’s say I have a file that looks like this:

# <access_type> <value>
1 value1
2 value2
1 value3
....

Now, let’s imagine that I only want to display values with access_type 1 (because they are users data). I can do something like that:

def print_value(1, value), do: IO.inspect(value)
def print_value(2, value), do: :ignore

But here, why are we only printing value with type 1 ? The code is not really explicit.
A better solution could be:

@user_type 1
@admin_type 2
def print_value(type, value) when type == @user_type, do: IO.inspect(value)
def print_value(type, value) when type == @admin_type, do: :ignore

Now the code is much more explicit. It is easy to understand that we do not want to display data belonging to an administrator.

But what if several modules need to define these access types. The best would be to define a module that exports these constants.
This is what SimpleEnum does:

defmodule MyEnums do
    import SimpleEnum, only: [defenum: 2]
    defenum :access_type, [user: 1, admin: 2]
end

def print_value(type, value) when type == access_type(:user), do: IO.inspect(value)
def print_value(type, value) when type == access_type(:admin), do: :ignore

Here, my example is probably not the best, because you can easily rewrite it without guard (with a case for example) but sometimes it is not possible.

Conclusion:
I think SimpleEnum is not intended to replace ecto_enum or the native Ecto.Enum module but rather to add compile time functionality to them.
In the documentation, there is an example where you can see SimpleEnum and the native Ecto.Enum working together.

thiagomajesk

thiagomajesk

Hi @ImNotAVirus! Cool project… Could you elaborate on how is simple_enum different from existing packages like ecto_enum and the native Ecto.Enum module? I’ve been using ecto_enum for some time now and have been meaning to migrate to the new native Ecto.Enum. I think a comparison would be great to understand the value proposition of your package. Cheers!

c4710n

c4710n

Compile-time checking! Love it.

Thanks for sharing.

Where Next?

Popular in Libraries Top

sasajuric
I’d like to announce a small library called boundaries. This is an experimental project which explores the idea of enforcing boundaries ...
New
treble37
Just looking for a little feedback on a tiny helper library I built - Sometimes I find the need to convert maps with atom keys to maps...
New
New
danschultzer
In short Plug n’ play OAuth 2.0 provider library. Just set up a resource owner schema with Ecto (your user schema), install the dependen...
New
tmbb
I’ve published the first version of my Makeup library. It’s a syntax highlighter for Elixir in the spirit of Pygments, Currently it highl...
New
woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
Jskalc
Hi! Today, after a couple weeks of development I’ve released v0.1 of LiveVue. It’s a seamless integration of Vue and Phoenix LiveView, i...
New
tmbb
PhoenixWS - Websockets over Phoenix Channels Source code on Github here: https://github.com/tmbb/phoenix_ws Phoenix channels are a great...
New
mischov
import Meeseeks.CSS html = HTTPoison.get!("https://news.ycombinator.com/").body for story &lt;- Meeseeks.all(html, css("tr.athing")) do...
New
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
magnetic
Hey :wave:t3: Elixir community, I’ve been learning Elixir, and working on some side projects. My editor of choice is VSCode, and althoug...
New

Sub Categories:

We're in Beta

About us Mission Statement