Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access Module attributes outside the module

Tags:

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?

like image 487
Sheharyar Avatar asked Jun 08 '16 21:06

Sheharyar


2 Answers

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.

like image 142
ventsislaf Avatar answered Nov 29 '22 02:11

ventsislaf


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.

like image 42
Davide Aversa Avatar answered Nov 29 '22 02:11

Davide Aversa