Closed Qqwy closed 5 years ago
_ => "Failure"
Can we assume this fat arrow beeing a copy and paste issue rather than a syntax error? I didn't realize it when posting my reply on the forum…
Patterns (matches) do not allow guards. You can't write x when is_list(x) = ...
. The same way you can't write def foo(x when is_list(x))
. Therefore one it won't work if you "hide" the when
inside a macro There was a discussion to allow so in the mailing list some time ago but we did no go forward with it due to the implications in terms of matching.
@josevalim However, guards are allowed in case-clauses and with-clauses. You can manually write x when is_list(x)
and it will compile and work as expected.
Yes, but case clauses are defined as match [with guards]
. Still, in a case clause, you can't do this:
{:value, your_ok_macro()}
It just doesn't compose the way you are thinking it does.
I do not expect that it would compose, but I did presume that a macro to the LHS of a ->
would be expanded to a match [with guards]
rather than just a match
.
Doesn't this line in elixir-clauses.erl
mean that the LHS of a ->
is not expanded at all? Or does that happen elsewhere?
And even if this is expected behaviour, the compiler error message is quite confusing.
It definitely expands on the left side of ->
. I agree the error message could be amended though to say it only allow macros and certain expressions.
I am confused by the following:
iex> require Example
iex> import Example
iex(3)> q = quote do
...(3)> case {:ok, 1} do
...(3)> ok() -> "Yay!"
...(3)> _ -> "Fail!"
...(3)> end
...(3)> end
{:case, [],
[
{:ok, 1},
[
do: [
{:->, [], [[{:ok, [context: Elixir, import: Example], []}], "Yay!"]},
{:->, [], [[{:_, [], Elixir}], "Fail!"]}
]
]
]}
iex(4)> q2 = Macro.expand(q, __ENV__)
{:case, [],
[
{:ok, 1},
[
do: [
{:->, [], [[{:ok, [context: Elixir, import: Example], []}], "Yay!"]},
{:->, [], [[{:_, [], Elixir}], "Fail!"]}
]
]
]}
iex> q == q2
true #huh?
I would expect Macro.expand
here to expand the ok()
on the left hand side of the ->
(in other words, expand the [{:ok, [context: Elixir, import: Example], []}]
), but it seems like it is kept as-is, and only expanded when the code is actually evaluated (e.g. Code.eval_quoted(q2)
).
What is going on here?
A macro inside a quote is not expanded when the quote is built. Also. Macro.expand does not traverse the tree. It expands only the root node.
Very interesting.
Expanding the quote ourselves as follows gives the result I would expect:
iex> q3 = Macro.prewalk(q, fn elem -> Macro.expand(elem, __ENV__) end)
iex> Code.eval_quoted(q3)
{"Yay!", []}
The one thing that confuses me, however, is that this is not the same as the result that is obtained when Elixir expands the macros automatically when the quote q
is evaluated.
Right, it is going to work in case as it does allow when at the root level side by side, but it is not a general construct. --
José Valimwww.plataformatec.com.br http://www.plataformatec.com.br/Founder and Director of R&D
Environment
Elixir & Erlang/OTP versions (elixir --version): Erlang/OTP 21 [erts-10.0.5] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] Elixir 1.8.1 (compiled with Erlang/OTP 20)
Operating system: Linux Manjaro
Situation
I have written about this problem on the forum before I started to expect it was a bug here.
Take the following macros. They are meant to slightly reduce boilerplate in common pattern-matches (like in
with
-ladders).ok()
could then be used like so:Expected behavior
When running this example, I'd expect
res
to equal"Yay!"
.Actual behaviour
Even stranger, when trying to use it inside another module:
compilation of the project fails with this error:
It seems like when a macro is passed to a
case
statement (or other pattern-match location;ok() = {:ok, 1}
,match?(ok(), {:ok, 1})
also fail with the same error) that the macro is not properly expanded.Oddly enough, when running
a = quote do ok() = {:ok, 1} end; Macro.expand(a, __ENV__)
it does not seem like the macro is expanded at that time, but only when the match is invoked later.