laiboonh
Trying to define my own if macro
defmodule Control do
defmacro my_if(expr, do: if_block, else: else_block) do
quote do
Control.do_my_if(unquote(expr), do: unquote(if_block), else: unquote(else_block))
end
end
defmacro my_if(expr, do: if_block) do
quote do
Control.do_my_if(unquote(expr), do: unquote(if_block), else: nil)
end
end
def do_my_if(expr, do: if_block, else: else_block) do
case (expr) do
result when result in [false, nil] -> else_block
_ -> if_block
end
end
end
iex(1)> c "control.exs"
[Control]
iex(2)> require Control
Control
iex(3)> Control.my_if(2==2, do: "true", else: "false")
"true"
What i don’t like about my implementation is that i would like to keep do_my_if as private function but this doesn’t compile if i do that.
I also don’t like the fact that i have to call Control.do_my_if in the macro body, why can’t i simply do do_my_if. During the AST expansion phase do_my_if should be totally legal shouldn’t it?
I looked at the Elixir source code elixir/lib/elixir/lib/kernel.ex at 5feec03db6a134371d9c0f60cc8873232659005e · elixir-lang/elixir · GitHub and the two points i mentioned seemed achievable. I don’t know what i am doing wrong. Any help is appreciated, thanks in advance.
Most Liked
NobbZ
unquote does not execute anything.
But, lets try to expanding your macro by hand. Perhaps we can enlighten you that way!
Your intitial version:
defmodule Control do
defmacro my_if(expr, do: if_block, else: else_block) do
quote do
Control.do_my_if(unquote(expr), do: unquote(if_block), else: unquote(else_block))
end
end
defmacro my_if(expr, do: if_block) do
quote do
Control.do_my_if(unquote(expr), do: unquote(if_block), else: nil)
end
end
def do_my_if(expr, do: if_block, else: else_block) do
case (expr) do
result when result in [false, nil] -> else_block
_ -> if_block
end
end
end
In the wollowing steps, I wont use the AST, but the human readable representation of the AST to simplify things a bit.
And now lets expand Controlö.my_if(false, do: IO.inpsect true, then: IO.inspect false:
First lets fill in the gaps of the Macro:
quote do
Control.do_my_if(unquote(false, do: unquote(IO.inspect true), then: unquote(IO.inspect false)))
end
Now lets do the unquote:
Control.do_my_if(false, do: IO.inspect true, then: IO.inspect false)
As you can see, we have finished expanding and compiling the macro. We have left a plain function call in the BEAM-byte code. Its arguments will be evaluated at runtime.
Now lets try the version of @OvermindDL1 which I won’t paste again, using the same snippet as above:
The first thing to mention is, that there is no quote in the macro definition, therefore the function is called at compile time! In that function is a quote, therefore wi will look at that now:
quote do
case unquote(false) do
result when result in [false, nil] -> unquote(IO.inspect false)
_ -> unquote(IO.inspect true)
end
end
And now unquote this:
case false do
result when result in [false, nil] -> IO.inspect false
_ -> IO.inspect true
end
As you can see, this version expands into a case-expression and not a function call. Also since do_my_if is called from inside Control during compiletime (not from another module during runtime as in your version), you should be able to even defp it in Overminds version.
Somewhere burried in the Macro module, there was a function or macro which was able to print the expanded sourcecode of a macro-call. That is very nice when debugging them.
NobbZ
That never happened ![]()
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








