虽然宏bind_quoted在第一次迭代时中断

问题描述 投票:0回答:1

也许我误解了bind_quoted的使用但是采用这个简单的while循环宏:

defmodule Loop do
  defmacro while(expression, do: block) do
    quote bind_quoted: [expression: expression, block: block] do
      for _ <- Stream.cycle([:ok]) do
        if(expression) do
          block
        end
      end
    end
  end
end

用法:

Interactive Elixir (1.8.0) - press Ctrl+C to exit (type h() ENTER for help) 
iex(1)> c "loop.ex" 
[Loop] 
iex(2)> import Loop
iex(3)> while 1 == 1 do 
...(3)> IO.puts "hi"
...(3)> end
hi

我希望无限循环“hi”,但只能获得一次迭代。如果我删除bind_quoted和简单的unquote()每个参数,它按预期工作。有任何想法吗?

谢谢

elixir
1个回答
0
投票

当你使用bind_quoted时,它会评估你传入的部分。所以IO.puts "hi"[expression: expression, block: block]中评估,而不是在你的if块中。你只是在那里注射:ok

作为一个例子,让我们定义一些除了评估传入内容之外什么都不做的宏:

defmodule AB do
  defmacro a(block) do
    quote bind_quoted: [block: block] do
      block
      block
      :ok
    end
  end

  defmacro b(block) do
    quote do
      unquote(block)
      unquote(block)
      :ok
    end
  end
end

忽略第一个警告,让我们用包含IO.inspect/1的块调用每个警告,看看会发生什么:

iex> require AB
iex> AB.a(IO.inspect(1 + 1))
... (some warnings)
2
:ok
iex> AB.b(IO.inspect(1 + 1))
2
2
:ok

在宏a/1中,sum + inspect在引用之外发生,所以它只被称为一次。在宏b/1中,总和+检查发生在我们不引用block的任何地方。

© www.soinside.com 2019 - 2024. All rights reserved.