alisinabh
Rust-like Enums in Elixir using macros - Good/Bad?
Hi everyone!
My colleagues and I ran into challenges with atoms and tuples in our Elixir codebases, especially when modeling domain states or structured data clearly.
Atoms are handy but also sometimes annoying. They’re global, easy to typo and need extra tests to ensure correctness. Not to mention converting strings safely to atoms isn’t very straightforward (and sometimes unsafe/unreliable even with String.to_existing_atom/1).
I had an idea to solve this and built a PoC library but to be honest I’m not entirely sure if the community would like it. Especially I’m curious to know if you see this in a codebase would it make understanding the code easier or more difficult?
Here is my PoC: (It is called Enuma and is available on Hex.pm if you want to try it out)
defmodule Shape do
use Enuma
defenum do
item :circle, args: [float()] # Circle with radius
item :rectangle, args: [float(), float()] # Rectangle with width and height
item :triangle
end
end
circle = Shape.circle(5.0) # = {:circle, 5.0}
require Shape
case shape do
# Can use them in pattern matching as they are compile time macros
Shape.circle(r) -> :math.pi() * r * r
# Above case clause is same as
# {:circle, r} -> :math.pi() * r * r
Shape.rectangle(w, h) -> w * h
Shape.triangle() -> raise "Triangle Area Not Supported!"
end
Generally what Enuma does it generating macros based on the enum definition. It also generates is_* guards that can be used in “when” clauses.
Back to the question
Here’s the thing: Enuma does slightly alter how you’d typically write Elixir code, which might make some developers uncomfortable or lead to mixed styles in codebases.
I’m curious:
-
Do you think libraries like Enuma enhance clarity, or do they risk complicating Elixir’s simplicity?
-
Would Enuma fit well into your projects, or does it feel too different?
-
How do you currently handle structured domain modeling in your code?
-
If you compare it with
Ecto.Enum, would you see useful benefits for your usecases?
I’m looking forward to your thoughts!
https://github.com/alisinabh/enuma/
Most Liked
hauleth
Good news for you all my fellow alchemists - it is built in:
defmodule Shape do
import Record
defrecord :circle, radius: nil
defrecord :rectangle, width: nil, height: nil
defrecord :triangle, []
end
circle = Shape.circle(radius: 5.0) # => {:circle, 5.0}
require Shape
case shape do
# Can use them in pattern matching as they are compile time macros
Shape.circle(radius: r) -> :math.pi() * r * r
Shape.rectangle(width: w, height: h) -> w * h
Shape.triangle() -> raise "Triangle Area Not Supported!"
end
garrison
I think this is a problem which is best solved by a type system. TypeScript’s string literal types solve a similar issue. Once you have type inference/checking for atoms the problems you describe should go away.
D4no0
I am not sure you should do that to begin with. What is the use-case where you are required to convert strings to atoms on a basis?
I’m not a big fan of using functions generated by macros in application code, it doesn’t play well with LSP and as the modules become more complex, it becomes a source of confusion. Maybe you could redesign the code to work around modules:
circle = Enuma.new(Shape, :circle, [5.0])
case shape do
Enuma.match(Shape, :circle, [radius]) -> ....
end
The way I did it a few times when I required enums was with functions:
def circle(radius) when is_float(radius), do: {:circle, radius}
case shape do
{:circle, radius} -> ....
end
While the macro version looks more organized, I think that simple functions are superior as you don’t tie your enum functions to a module, it amounts to easier refactors and you can cram a unlimited number of functions in a module. I am not a big fan of how functionality like defstruct couples with the module, but I am not entirely sure at the same time how a readable alternative would look like.
As it currently stands, it seems too alien for most of the codebases I worked with. Nonetheless, I think such a library might be a great addition to the ecosystem.
Popular in Discussions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








