简体   繁体   中英

Elixir macros, quoting and unquoting

I'm learning Elixir and experimenting with the Macro system.
I am trying to make a variable available to a block that will be wrapped with other code by a macro, but I am clearly doing something wrong.

This is my starting point macro, which works. I am ware that there are more idiomatic ways to iterate in Elixir, and I wrote it just as an exercise:

defmodule Loops do
  defmacro times(n, [do: block]) do
    quote do
      Enum.each(1..unquote(n), fn(_) -> unquote(block) end)
    end
  end
end

import Loops

times 3 do
  IO.puts "Hello!"
end

Now, what I want to accomplish is being able to reference the counter from the do block. So, if I give a proper name to the fn argument:

Enum.each(1..unquote(n), fn(_) -> unquote(block) end)
# becomes:
Enum.each(1..unquote(n), fn(counter) -> unquote(block) end)

I would like to be able to do something like this:

times 3 do
  IO.puts "Hello! iteration n: #{counter}"
end

This however doesn't work and raises a CompileError because of undefined function counter/0 . The error is raised from the do block I passed to the macro invocation , which I find a bit counterintuitive because I thought that the block would be placed in the expanded code when calling unquote() .

Am I approaching the problem in the wrong way, or is this simply not possible?

You can use Kernel.var!/2 to make counter an "unhygienic" variable. This will make sure it is available to the generated code without being renamed by Elixir's macro system.

defmodule Loops do
  defmacro times(n, [do: block]) do
    quote do
      Enum.each(1..unquote(n), fn(var!(counter)) ->
        unquote(block)
      end)
    end
  end
end

defmodule Main do
  require Loops

  def main do
    Loops.times 3 do
      IO.puts "Hello! iteration n: #{counter}"
    end
  end
end

Main.main

Output:

Hello! iteration n: 1
Hello! iteration n: 2
Hello! iteration n: 3

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