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

blatyo
https://www.conduitframework.com/ The best overview for how things are tied together is this presentation. Modules and functions are pre...
New
Qqwy
Solution is a library to help you with working with ok/error-tuples in case and with-expressions by exposing special matching macros, as ...
New
OvermindDL1
Been making an MLElixir thing (not released yet…) for fun in spare time in the past day. I’m just trying to see how much I can get an ML...
132 13487 106
New
Eiji
ExApi is a library that I’m developing now and hope release soon This library will allow to: list all apis list all api implementation...
New
alisinabh
Hey everyone i’ve developed a library for Jalaali calendar for elixir which supports converting Gregorian dates to Jalaali and vice vers...
New
tmbb
I’ve decided to create this topic to discuss optimization possibilities for something like Phoenix LiveView. I’ve created this topic unde...
New
mplatts
With HEEX released we decided to start a components library using Tailwind CSS - check it out here: Petal Components. We also have a boi...
New
RobertDober
Earmark is a pure-Elixir Markdown converter. It is intended to be used as a library (just call Earmark.as_html), but can also be used as...
239 11851 134
New
riverrun
I’ve just released version 3 of Comeonin, a password hashing library. The following small changes have been made: changes to the NIF c...
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

Other popular topics Top

lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
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
AstonJ
You’re a programmer, so you don’t need spoon feeding with the conventional drivel about “this is an integer.” No. You need to know what’s...
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
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New

Sub Categories:

We're in Beta

About us Mission Statement