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
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
Eh, not really like assembler, it is the very definition of AST. Elixir’s AST. ![]()
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. ![]()
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
@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.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









