What is the return value of quote block?

Hi all
I would be very happy, if someone could explain me, what does quote return in a defmacro!


1 Like

quote does transform the code in the “do-block” into an Abstract Syntax Tree, which then can be injected into your program.

Thats it.

If this is not answering your original question, please be more specific and tell us your original question :wink:

1 Like

Take a look on the code, this is the macro definition

     defmodule ControlFlow do
         defmacro unless(expression, do: block) do
            quote do
              if !unquote(expression), do: unquote(block)

So when I write in the iex shell

     iex(16)> ControlFlow.unless 2 == 5, do: "block entered"

I am expecting an ast as the result but I’ve got block entered value. So when I write

     iex(3)> quote do: if 5 == 5, do: "Hello Foo"
                {:if, [context: Elixir, import: Kernel],
                [{:==, [context: Elixir, import: Kernel], [5, 5]}, [do:"Hello Foo"]]}  

in the shell, I’ve got an ast not the value.

quote does return an ast. This happens at compile time.

So given your macro above, the following happens.

  1. You type ControlFlow.unless 2 == 5, do: "block entered" into iex.
  2. There happens some magic, which involves compiling the snippet in a given context and then executing it.
  3. During that compile-phase that call is “expanded” to whatever quote returns, {:if, [context: Elixir, import: Kernel], [{:==, [context: Elixir, import: Kernel], [5, 5]}, [do:"Hello Foo"]]} in this case.
  4. Then in the execution-phase that AST is executed and returns "block entered".

Elixir is a compiled language, and as such you always have to distinguish compile time and run time.

1 Like

First of all, thanks explanation, now I have more question, can you say more about compile time and run time.
So when I compile a file, that contains macros with following statement

     iex(1)> c "unless.exs"

and then require it:

     iex(2)> require ControlFlow

and at the end call the macro.

     ControlFlow.unless 2 == 5, do: "block entered"  

How to code will compile and run?

I’ve found a macro definition in internet

defmacro example do
      {:+, [], [1, 2]}

I return here an ast and when I call the macro, as the result I’ve got 3. That means, the shell will executed the returned ast?

As everything in Elixir, that is an expression, not a statement but an expression and returns the list of modules defined in the given file.

Since it is easier to explain, I will assume, that you have your module ControlFlow as shown above and another one ControlFlowUsage:

# control_flow_usage.ex
defmodule ControlFlowUsage do
  require ControlFlow

  def foo do
    ControlFlow.unless 2 == 5, do: "block entered"
  1. Compile via elixirc control_flow_usage.ex
    1.1. The compiler will stumble over the call to ControlFlow.unless/2 and execute it.
    1.2. The returned AST will be inserted instead of the Macro-call, this is called “expansion”.
    1.3. The AST of the whole module is transformed to BEAM-Bytecode and written to disc.
  2. You load that module into IEx and call ControlFlowUsage.foo()
    2.1. The Bytecode of the function is executed.
    2.2. The executed bytecode corresponds to the elixir expression if !(2 == 5), do: "block entered"
    2.3. You will get the value "block entered" in return

This is a simplified process, in reality the process is more complex and optimisation processes might kick in.

This is pretty much the same as defmacro example, do: quote(do: 1 + 2), but less readable. In my opinion at least. Whatever gets returned by a macro is injected into the AST and if you return an invalid AST you will have a hard time to debug your error messages later on.

1 Like

Thanks so much for your wonderful explaination.
I love elixir, that because annoying you with my question :slight_smile:

Thanks …

1 Like

I don’t consider questions beeing annoying. There are always three people learning from an answered question:

  1. The one who asks the question can learn from the given answer
  2. The one who answers hardenes his knowledge by applying it to some possibly new context or abstracting/generifying knowledge from other fields
  3. The one who has a similar problem can find the question and the answer and hopefully apply whats said/transfer it to his specific problem

So feel free to ask further questions whenever you have them.