[ANN] Const - helper for defining constants

129 views
Skip to first unread message

Michele Balistreri

unread,
Aug 30, 2016, 1:52:44 AM8/30/16
to elixir-lang-talk
Hi all,

after reading the topic at http://stackoverflow.com/questions/33851536/how-do-you-define-constants-in-elixir-modules I decided to use the approach taken by wxErlang. I found this to be a little verbose, especially since I also needed a list of all constants and an easy way to convert from the integer value to the associated atom.

So I created the const package, which allows you to write this:

defmodule Status do
  use Const, [:queued, :processed, :sent]
end

and obtain this

defmodule Status do
  def queued, do: 0
  def processed, do: 1
  def sent, do: 2
  def all, do: [queued: 0, processed: 1, sent: 2]
  def by_value(val) do
    # returns the atom from the integer value. In case of duplicated values, the fist
    # associated atom is returned
  end
end

You can also give a keyword list if you need specific values, and even a list where some elements are just atoms and some are tuples. The behavior in this case will be like for C enums.


Hope it can be useful!

Regards,
Michele Balistreri
Bitgamma OÜ

OvermindDL1

unread,
Aug 30, 2016, 10:14:19 AM8/30/16
to elixir-lang-talk
I actually do use a lot of `defmacro something, do: 42` for enums operating with a remote system, this would be convenient.  :-)

Have you thought about making it a macro (be sure to escape the return in case they want the value to be a tuple or so), that way it can be used in more areas and in matching?

Michele Balistreri

unread,
Aug 31, 2016, 1:53:49 AM8/31/16
to elixir-lang-talk
Macro would also be possible, but in that case you would have to require the module to use its constants. I agree however that being useable in guards is a good point. PRs are always welcome. 

Regarding allowing non-integer values, I do not know if it makes much sense. The entire point of defining constants this way is to be able to interface with external services/serialization. If you can use Elixir terms then you probably do not need this. Or am I missing something?

OvermindDL1

unread,
Aug 31, 2016, 10:01:08 AM8/31/16
to elixir-lang-talk
Could be a binary to match to/against, a tuple that defines some internal format, etc...

Michał Muskała

unread,
Sep 2, 2016, 10:41:05 AM9/2/16
to elixir-l...@googlegroups.com
While I can see the appeal of such solutions I'd say the usual way to deal with such things is to use atoms and do the conversion on the system boundry. You convert to atom the incoming params as soon as you can, and you convert back to the integer value just before sending it out.
In general you only need two functions: to_atom/1 and from_atom/1, you can even automate generating them with a macro:

@mapping [foo: 1, bar: 2]

for {key, value} <- @mapping do
def to_atom(unquote(value)), do: unquote(atom)
def from_atom(unquote(atom)), do: unquote(value)
end

This has the huge advantage of a meaningful value for debugging and maintaining the same speed (atoms are more-or-less integers at runtime).

Michał.
> --
> You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-ta...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-talk/9ff0db4b-6181-421d-8ed4-f5da8b5910d4%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

signature.asc
Message has been deleted

Michał Muskała

unread,
Sep 6, 2016, 10:09:38 AM9/6/16
to elixir-l...@googlegroups.com

> On 02 Sep 2016, at 17:10, OvermindDL1 <overm...@gmail.com> wrote:
>
> Yeah that macro would be quite useful. This Const I would use on such bounds, I just hate magic numbers in the code even at the bounds, hence helper methods.

I fully agree with that, the way I would handle that is with some macros to generate the conversion functions:

statuses = [draft: 0, pending: 1, complete: 2, published: 3]

for {atom, int} <- statuses do
def to_status(unquote(int)), do: unquote(atom)
def from_status(unquote(atom)), do: unquote(int)
end

This gives you two functions and no free-floating magic numbers.

Michał.
signature.asc

Ben Wilson

unread,
Sep 6, 2016, 10:13:24 AM9/6/16
to elixir-lang-talk
Why not just have the atom as both the argument and the value? They're little more than an integer comparison anyway.

Michał Muskała

unread,
Sep 6, 2016, 10:16:14 AM9/6/16
to elixir-l...@googlegroups.com

> On 06 Sep 2016, at 16:13, Ben Wilson <benwil...@gmail.com> wrote:
>
> Why not just have the atom as both the argument and the value? They're little more than an integer comparison anyway.
>

I'm not sure I understand. The snippet I posted is to convert some externally incoming integer values to atoms for processing inside the system, and then convert them back for some sort of reply or further processing in other external systems.

Michał.

signature.asc

Onorio Catenacci

unread,
Sep 6, 2016, 12:55:40 PM9/6/16
to elixir-lang-talk
I can't speak for Ben but I'd guess he was missing the part about interfacing with external code.  I missed it initially too. :)

Michele Balistreri

unread,
Sep 6, 2016, 3:15:23 PM9/6/16
to elixir-lang-talk
While I can see the appeal of such solutions I'd say the usual way to deal with such things is to use atoms and do the conversion on the system boundry. You convert to atom the incoming params as soon as you can, and you convert back to the integer value just before sending it out. 

Indeed, the approach you suggest is probably better in many cases. I will probably extend const in the near future to also allow generating modules with this easily. Thanks for the feedback!
Reply all
Reply to author
Forward
0 new messages