简体   繁体   中英

Open modules in 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?

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, ie 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM