Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Open modules in Elixir?

Tags:

elixir

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?

like image 981
iconoclast Avatar asked Feb 03 '15 05:02

iconoclast


1 Answers

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.

like image 125
Patrick Oscity Avatar answered Oct 13 '22 11:10

Patrick Oscity