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.