Elixir: modifying the attribute value of a module

Is it possible to achieve the behavior below when you try to change the value of a module attribute to change the behavior of module methods?

defmodule Adder do @num_to_add 10 def addTo(input), do: input + @num_to_add end IO.inspect Adder.addTo(5) # Prints 15 Adder.num_to_add = 20 IO.inspect Adder.addTo(5) # Expect it to print 25 

It gives an error below

 ** (CompileError) hello.exs:8: cannot invoke remote function Adder.num_to_add/0 inside match (elixir) src/elixir_clauses.erl:26: :elixir_clauses.match/3 

If this is not possible (since everything in the Elixir must be unchanged), is there any elixir way to achieve this behavior?

+7
elixir
source share
2 answers

This is not possible because attributes exist only until this particular module is compiled. When the module is compiled, all attributes are embedded and forgotten, so at the point where you can call functions from this module, changing the attributes is no longer possible.

This code should show this more clearly:

 defmodule Test do @attr 1 @attr 2 def attr do @attr end end IO.inspect Test.attr # => 2 Module.put_attribute(Test, :attr, 3) IO.inspect Test.attr # => ** (ArgumentError) could not call put_attribute on module Test because it was already compiled 

Please note that you can change the value of the attribute until the module has been compiled (for example, in the module) by simply setting it again, as I here, when setting @attr to 2 .

By the way, what you are trying to achieve can be easily done with Agent :

 defmodule Storage do def start_link do Agent.start_link(fn -> 10 end, name: __MODULE__) end def add_to(input) do Agent.get_and_update(__MODULE__, fn (x) -> {x + input, x + input} end) end end Storage.start_link IO.inspect Storage.add_to(5) # => 15 IO.inspect Storage.add_to(5) # => 20 

A good rule of thumb in Elixir is that whenever you need to track some kind of volatile state, you will need to process the process that will consist.

+13
source share

Elixir's modules are not designed to store states like objects / classes in object-oriented programming languages. The module attribute is more like a constant, and its value cannot be changed after the module has been compiled and inaccessible from the outside, if it is not opened through the function.

Pawel offers some good alternatives to how to achieve this behavior. Here is another, which is simpler, since you do not need to save state in another process.

 defmodule Adder do @num_to_add 10 def addTo(input, num_to_add \\ @num_to_add), do: input + num_to_add end 

In the above approach, we simply set the module attribute as the default value. If we want to override this, just specify the second argument.

+4
source share

All Articles