You're right: it has been covered extensively but, imho, not deeply discussed.
Actually we meet this situation starting from an our macro implementation.
#!/usr/bin/env elixir
defmodule DoSomethingMacro do
defmacro __using__(_) do
quote do
import DoSomethingMacro, only: :macros
end
end
defmacro do_something_on_flag(flag, do: block) when is_boolean(flag) do
quote do
case unquote(flag) do
true -> unquote(block)
_ -> IO.puts "do other things"
end
end
end
end
defmodule DoSomethingMacroTest do
use DoSomethingMacro
def run() do
list = [:one]
do_something_on_flag(true) do
list = [:two | list] # BTW: why not unsafe variable warning?
end
IO.inspect list # unsafe variable warning
# list is [:two, :one]
do_something_on_flag(false) do
list = [:three | list] # unsafe variable warning
end
IO.inspect list # unsafe variable warning
# list is [:two, :one]
list =
do_something_on_flag(false) do
[:fourth | list] # unsafe variable warning
end
IO.inspect list
# list is :ok (due to IO.puts "do other things")
end
end
DoSomethingMacroTest.run
In our do_something_on_flag (used as a common library) implementation should not assume that block must return something.
When we write
list =
do_something_on_flag(false) do
[:fourth | list] # unsafe variable warning
end
we break the list variable.
Moreover, if we modify the do_something_on_flag macro in order to manage alse the else clause, we are forced to write something like this
list =
do_something_on_flag(false) do
[:fourth | list]
else
list
end
First: this is useless for us because our macro must apply a standard behaviour in an hypothetical
else clause and deny the caller to have a different behaviour.
Second: in my opinion this is an outrage of Elixir elegance and conciseness.