Open modules in Elixir?

泪湿孤枕 提交于 2019-12-23 07:40:05

问题


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?


回答1:


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.



来源:https://stackoverflow.com/questions/28291854/open-modules-in-elixir

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!