Ruby has open classes, which are very handy (though reviled by some), and Elixir borrows heavily from Ruby, so I expected Elixir to allow me to reopen a module and add macros to it after having closed it, but this did not work in the way I tried it. Is there some way to do this? Is this feature available yet in Elixir?
To make this concrete, let's take an example from Chris McCord's Metaprogramming Elixir:
defmodule Math do
defmacro say({:+, _, [lhs, rhs]}) do
quote do
lhs = unquote(lhs)
rhs = unquote(rhs)
result = lhs + rhs
IO.puts "#{lhs} plus #{rhs} is #{result}"
result
end
end
defmacro say({:*, _, [lhs, rhs]}) do
quote do
lhs = unquote(lhs)
rhs = unquote(rhs)
result = lhs * rhs
IO.puts "#{lhs} times #{rhs} is #{result}"
result
end
end
end
If I then add a macro for subtraction
defmodule Math do
defmacro say({:-, _, [lhs, rhs]}) do
quote do
lhs = unquote(lhs)
rhs = unquote(rhs)
result = lhs - rhs
IO.puts "#{lhs} minus #{rhs} is #{result}"
result
end
end
end
I get a warning that I'm redefining module Math, and the initially defined macros fail. So obviously my approach is not the right one, but is there another approach that can accomplish the same goal?
You cannot re-open a module. Since Elixir is a compiled language, the way from source code to an executable representation is one-way. The mechanisms that alter code, such as macros, are evaluated during compilation time. However, Elixir allows you to do hot code swapping, i. e. you can replace a compiled module during runtime.
I think this has some interesting implications. You get the ability to change the program at runtime – in practice, you control the source code and so you can swap out individual parts of a module without changing others. Still, I like the idea of having everything in one place. In Elixir, when you look at a module you know exactly what you've got*.
So if this question is only about a convenient development workflow – the closest you will come to Ruby's ability to add methods to a class is to save your module in a file and re-compile it from iex as you go.
* Strictly speaking, you might not know exactly what code is running by just looking at a module. When you are using protocols, you kind of need to look at all of the protocol implementations as well, which may be scattered throughout your codebase, or, even worse, they may be distributed across multiple applications.
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