I have this module in elixir with an attribute:
defmodule MyAwesomeModule do @awesome_number 7 # other stuff... end
I'm unable to access @awesome_number
outside the module. I've tried using the Module.get_attribute/2
method, but it throws this error:
iex(79)> Module.get_attribute(MyAwesomeModule, :awesome_number) ** (ArgumentError) could not call get_attribute on module MyAwesomeModule because it was already compiled (elixir) lib/module.ex:1101: Module.assert_not_compiled!/2 (elixir) lib/module.ex:1016: Module.get_attribute/3
So right now, I'm wrapping the module attribute in a method to access it, but it doesn't really make sense to me. I could simply use the method and stop using the attribute all together:
defmodule MyAwesomeModule do @awesome_number 7 def awesome_number, do: @awesome_number # other stuff... end
So my question is, is there a better / proper way of doing this?
AFAIK there is no way to access the module attributes outside of the given module. Defining a function to expose the module attribute is the way to go, just what you are already doing.
There still could be a good reason to keep the module attribute, instead of just using the function without the module attribute. It depends on the context. Keep in mind that the value stored in the module attributes is calculated at compilation time. That being said you could have different reasons to use or not to use the module attribute. Let's look at the following examples:
If the awesome_number
have to be random generated every time when it's accessed, you have to go just with a function.
If the awesome_number
needs to be computed (long time) and it doesn't have to change its value, then going with the module attribute + function to expose it, is the way to go.
Edit:
There is more to module attributes from what I've said earlier. They perform better than just functions. Here is an example and a quote from the elixir docs:
defmodule MyServer do @my_data 14 def first_data, do: @my_data @my_data 13 def second_data, do: @my_data end MyServer.first_data #=> 14 MyServer.second_data #=> 13
Notice that reading an attribute inside a function takes a snapshot of its current value. In other words, the value is read at compilation time and not at runtime. As we are going to see, this makes attributes useful to be used as storage during module compilation.
Using them with Module.register_attribute/3
(https://hexdocs.pm/elixir/Module.html#register_attribute/3) and especially with the accumulate: true
option, makes them useful in more ways.
What I want to say is that they can be more useful than just being used as a constant.
There is a way of "cheating" by using use
and macros. Look at this example.
For instance suppose you define a module as:
defmodule AwesomeLibrary do defmacro __using__(_) do quote do def print(s), do: IO.puts(s) end end end
Then, in some module you can use the use
keyword in this way.
defmodule TestLibrary do use AwesomeLibrary end
The effect is that everything defined in the __using__
block is copied in the new module at compile time. So, in that case, you can use TestLibrary.print
even if print
is defined in another module.
This can be used to 'copy constants' too:
defmodule AwesomeLibrary do defmacro __using__(_) do quote do @awesome_number 7 def print(s), do: IO.puts(s) end end end
An example of this that you can look at is the Timex library. It uses a dedicated module for constants that is imported whenever it is needed.
This seems to me the nicer way to share constants definitions around a big codebase.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With