apoorv-2204

apoorv-2204

How do you handle enums in your codebase?

The Issue of Enums in Elixir

Elixir doesn’t have a native enum construct, so we usually rely on atoms, strings, or macros to represent named constants.

Observed Issues

String vs Atom:

Some contexts (like pattern matching or internal logic) prefer atoms, while others (like database fields or API responses) require strings.
Maintaining consistency across both can be cumbersome — I eventually prefer enums as strings for simplicity.

Compile-Time vs Runtime Checks:

We can emulate enums using function calls, but not within guards or compile-time contexts.
Macros can provide compile-time validation, but require the require keyword, which reduces simplicity.
There’s no built-in mechanism for enforcing enum constraints at both compile and runtime.

Questions:

How do you handle enums in your codebase?

Do you rely purely on atoms and type specs, or use macro-based libraries for safer compile-time checking?

Would a lightweight, language-level enum-like abstraction make sense for Elixir?

Most Liked

mudasobwa

mudasobwa

Creator of Cure
defmodule MyEnum do
  defmacro sigil_MYENUM({:<<>>, _, [value]}, [] = _modifiers) do
    case __CALLER__.context do
      :match ->
        if value in ~w[one two], do: value, else: raise(value)
      _ ->
        if value in ~w[one two], do: value, else: raise(value)
    end
  end
end

defmodule TestMyEnum do
  import MyEnum

  def test(value) do
    IO.puts(~MYENUM[one])
    IO.puts(~MYENUM[two])

    case value do
      ~MYENUM[one] -> IO.puts("ONE MATCHED")
      ~MYENUM[two] -> IO.puts("TWO MATCHED")
    end

    ~MYENUM[three]
  end
end

This example is a bit overfilled with some shenanigans, but it’d give you a great start.

garrison

garrison

Actually, one thing that does bother me is that there are a few different ways to implement constants and they all have different tradeoffs. I think this is a bit unintuitive.

@my_enum [:first, :second, :third]
def my_enum, do: [:first, :second, :third]
defmacro my_enum, do: [:first, :second, :third]

The third one is the only way to do pattern matching on constants from another module, yet that is the one which is not documented.

garrison

garrison

Of course for database fields Ecto does support enums. For other stuff I generally use atoms and pattern matching. I wouldn’t necessarily be opposed to a native Enum construct but pattern matching and a public constant for enumeration gets you pretty much all of the way there.

I think the type system will help a lot, here (and Dialyzer already helps a bit). I always liked the string literal types in TS. We generally use atoms, but it’s the same idea. Types are most useful for tooling rather than type-checking (i.e. autocomplete).

Last Post!

Asd

Asd

String comparison is linear and is always slower than atom comparison

Use tools which provide specs around APIs and databases. Consider GraphQL API with Absinthe or HTTP API with Oaskit, OpenApiSpex

What do you mean by “compile time checks”? You want to check that some function is always called only with some atoms? Dialyzer does that to some extent, but having stricter compile time checks requires static typing

I find this really funny

Where Next?

Popular in Questions Top

nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
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
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 44139 214
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New

We're in Beta

About us Mission Statement