Brushing up on Elixir after a couple of years, and want to properly understand its metaprogramming capabilities this time, but getting stuck. The 21. Optional syntax sheet is great (I don’t remember that chapter when I first learned Elixir, kudos!) but I still have some questions after experimenting in IEx.
I’m pretty sure I’m missing something basic and/or probably confusing certain constructs for others, but here it goes:
1. What does identifier M
below expand to?
The statement found everywhere that module names are simply atoms is good to know, but it does not seem to be the whole story. From a new IEx shell:
iex(1)> defmodule(:M, [{:do, def(f, [{:do, 3}])}])
iex(2)> M.f
** (UndefinedFunctionError) function M.f/0 is undefined (module M is not available)
M.f()
iex(2)> :M.f
3
iex(3)> quote do: :M.f
{{:., [], [:M, :f]}, [no_parens: true], []}
iex(5)> defmodule(M, [{:do, def(f, [{:do, 7}])}])
iex(7)> M.f
7
iex(8)> quote do: M.f
{{:., [], [{:__aliases__, [alias: false], [:M]}, :f]}, [no_parens: true], []}
iex(16)> M == :'Elixir.M'
true
I know that M
is an alias of Elixir.M
, which is the atom :'Elixir.M
’ but what is happening with :M
?
2. What does f
expand to in the def/2
call?
There are some clues from the console, but it only shows a part of the picture:
iex(17)> defmodule(M, [{:do, def(:f, [{:do, 9}])}])
** (CompileError) iex:17: invalid syntax in def :f
iex:17: (module)
iex(17)> defmodule(M, [{:do, def(f, [{:do, 9}])}])
{:module, M, <<...>>, {:f, 0}}
iex(18)> defmodule(M, [{:do, def(f(a,b), [{:do, 9}])}])
{:module, M, <<...>>, {:f, 2}}
Only after writing all this down did I realize that I may have never understood that M
and f
above are just identifiers (i.e., part of the syntax of Elixir) that have different AST representations, just like any other expression. Thus my questions may not make sense at all as M
and f
cannot be expected to be expanded into anything else as they are part of the syntax of the language.
iex(56)> quote do: ModuleNotDefined
{:__aliases__, [alias: false], [:ModuleNotDefined]}
iex(60)> quote do: non_existing_variable_or_function_name
{:non_existing_variable_or_function_name, [if_undefined: :apply], Elixir}
Thank you in advance for dispelling my confusion and any links to posts or the relevant source snippets is appreciated!