Bad Ideas : Elixir Global Getter/Setter

Continuing with the theme of things you shouldn't do and weren't going to do anyway, today I will illustrate creating global get and set functions.

I will use a simple macro to create set/1 . The macro will define a module with a get/0 function.

defmodule GetterSetter do defmacro set(val) do quote do defmodule Get do def get() do unquote(val) end end end end def get(), do: Get.get() end

All that nesting should clue you in that this is bad practice and also dumb.

Anyway the macro was necessary because of scoping. We could define the module inside a function just as well, but the value passed into the function would not be available within the defmodule 's do block's scope. Hence unquote

Trying it out:

iex> import GetterSetter nil iex> set(8) {:module, Get, <<70, 79, 82, 49, 0, 0, 4, 160, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:get, 0}} iex> get() 8 iex> set(:foo) iex: warning: redefining module Get {:module, Get, <<70, 79, 82, 49, 0, 0, 4, 164, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:get, 0}} iex> get() :foo

Get some nice warnings telling us we are well outside expected practice, but get/0 and set/1 work beautifully. And because module definitions are available to all processes on a node the values are available globally.

iex> pid = spawn fn -> receive do :get -> IO.puts(get()) end end #PID<0.257.0> iex> set(:horsefeathers) iex: warning: redefining module Get {:module, Get, <<70, 79, 82, 49, 0, 0, 4, 168, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:get, 0}} iex> send(pid, :get) horsefeathers :get

Best practice would be to use GenServer or ets . As the warning indicates redefining modules should not be done and I assume it is not an error only because it is useful when experimenting in and iex session.

On the other hand the ability to programatically generate modules and especially function definitions within modules is fantastic. Have a look at String.Unicode to see what I mean.

Update

I have been informed that the above technique, wile undoubtedly a hack, can be useful. It is used in mochiweb for example. In the docstring they link this post on the erlang-questions mailinglist by way of explanation: