Пока макрос bind_quoted прерывается на первой итерации - PullRequest
0 голосов
/ 25 марта 2019

Возможно, я неправильно понимаю использование 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 () для каждого аргумента, он будет работать как положено.Есть идеи?

Спасибо

1 Ответ

0 голосов
/ 25 марта 2019

Когда вы используете 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 проверка суммы + происходит вне кавычек, поэтому она вызывается только один раз. В макросе b/1 сумма + проверка происходит везде, где мы не цитируем block.

...