laiboonh

laiboonh

Can someone enlighten me on this piece of code?

This is a code snippet from the metaprogramming elixir book.

defmacro test(description, do: test_block) do
    test_func = String.to_atom(description)
    quote do
      @tests {unquote(test_func), unquote(description)}
      def unquote(test_func)(), do: unquote(test_block)
    end
  end

Can someone enlighten me on what is def unquote(test_func)(), do: unquote(test_block) doing? How is it even legal syntax??? I tried it out on iex:

iex(67)> defmodule Test do
...(67)> def test(a)(), do: a
...(67)> end
** (CompileError) iex:68: invalid syntax in def test(a)()
    iex:68: (module)

Most Liked

h4cc

h4cc

unquote is a function to “create code from data” simply said.

When this is executed
def unquote(test_func)(), do: unquote(test_block)

the result will be
def :description_as_atom(), do: [list, of, test, blocks]
which will be valid code for the compiler.

Short: unquote will mostly be called in macros to generate code from parameters (which is data at compile time), so only the result of unquote will stay afterwards.

OvermindDL1

OvermindDL1

Eh, not really like assembler, it is the very definition of AST. Elixir’s AST. :slight_smile:

The compilation process goes: Elixir → Elixir’s AST (what quote gives you) → Erlang (Abstract Format, basically it’s AST) → Core Erlang → BEAM
With a variety of translators along the way too. :slight_smile:

Basically take this Elixir:

defmodule :tester do

  def hi, do: "there"

end

To this Elixir AST:

{:defmodule, [context: Elixir, import: Kernel],
 [:tester,
  [do: {:def, [context: Elixir, import: Kernel],
    [{:hi, [context: Elixir], Elixir}, [do: "there"]]}]]}

To this Erlang:

-module(tester).
-export([hi/0]).
hi() -> <<"there">>.

To this Core Erlang:

module 'tester' ['hi'/0,
                 'module_info'/0,
                 'module_info'/1]
    attributes []
'hi'/0 =
    %% Line 3
    fun () ->
        #{#<116>(8,1,'integer',['unsigned'|['big']]),
          #<104>(8,1,'integer',['unsigned'|['big']]),
          #<101>(8,1,'integer',['unsigned'|['big']]),
          #<114>(8,1,'integer',['unsigned'|['big']]),
          #<101>(8,1,'integer',['unsigned'|['big']])}#
'module_info'/0 =
    fun () ->
        call 'erlang':'get_module_info'
            ('tester')
'module_info'/1 =
    fun (_cor0) ->
        call 'erlang':'get_module_info'
            ('tester', _cor0)

To this BEAM Assembly:

00007F031584F908: i_func_info_IaaI 0 tester hi 0
00007F031584F930: move_return_c <<"there">>

00007F031584F940: i_func_info_IaaI 0 tester module_info 0
00007F031584F968: move_cr tester r(0)
00007F031584F978: allocate_tt 0 1
00007F031584F988: call_bif_e erlang:get_module_info/1
00007F031584F998: deallocate_return_Q 0

00007F031584F9A8: i_func_info_IaaI 0 tester module_info 1
00007F031584F9D0: move_rx r(0) x(1)
00007F031584F9E0: move_cr tester r(0)
00007F031584F9F0: allocate_tt 0 2
00007F031584FA00: call_bif_e erlang:get_module_info/2
00007F031584FA10: deallocate_return_Q 0

And that is what is loaded by the VM.

Eiji

Eiji

@laiboonh: Inside quote do_block your expressions are quoted.
For example:

defmodule Example do
  defmacro sample do
    IO.inspect quote do: a
    :ok
  end
end
require Example
Example.sample
# {:a, [], Example}

Similarly test_func and description are quoted too.
Simple explanation for new developer is that unquote converts quoted expressions (AST) to their “normal” form.
For example:

defmodule Example do
  defmacro sample(a) do
    quote do
      unquote(a)
    end
  end
end
require Example
Example.sample(5)
# returns 5 instead of: {:a, [], Example}

So your line is “translated” from Elixir AST:

def {:test_func, [], ModuleName}(), do: {:block, [], ModuleName}

to “normal” code like:

def test_func(), do: test_block

You can see how it looks by inspecting variables in quote do_block without unquote part.
Please see: Quote and unquote for more informations.

Where Next?

Popular in Questions Top

siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
mgjohns61585
Could someone help me? I’m making my first elixir program, number guessing game. I can’t figure out how to convert the user’s guess from ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement