Whitespace ruined my life (subtle compile error)

74 views
Skip to first unread message

J David Eisenberg

unread,
May 20, 2013, 11:24:16 AM5/20/13
to elixir-l...@googlegroups.com
Consider this code, which does not work:

defmodule MyMacro do
  defmacro unless(clause, options) do
    quote do: if (!unquote(clause), unquote(options))
  end
end


elixirc my_macro.ex
== Compilation error on file my_macro.ex ==
** (SyntaxError) /home/david/testor/my_module.ex:3: syntax error before: ')'


But this code does work:

defmodule MyMacro2 do
  defmacro unless(clause, options) do
    quote do: if(!unquote(clause), unquote(options))
  end
end

elixirc my_macro2.ex
Compiled my_macro2.ex

The only difference is that I have a blank after "if" in the non-working version.

I am sure that the compiler is supposed to work that way, and there is a very good reason for it working that way. I'm curious to know what that reason is.

José Valim

unread,
May 21, 2013, 5:02:51 AM5/21/13
to elixir-l...@googlegroups.com
Consider this problem:

    float (42) |> IO.inspect

What would you expect IO.inspect to print, 42 or 42.0?

If we consider the above translates to "float(42) |> IO.inspect", it should print 42.0.
However, if we consider it as "float((42) |> IO.inspect)", it should print 42.
In both cases, the result is 42.0 though.

In other words, the behaviour you see is a consequence of optional parenthesis and Elixir chooses the above to translate to "float((42) |> IO.inspect)".

This means that, when there is a space in between the function name and the parenthesis, Elixir considers the parenthesis apply to the first argument and not the function call. So:

    float (42)

actually maps to:

    float((42))

This means that:

    if (this, do: that)

Translates to:

    if((this, do: that))

And the following:

    (this, do: that)

doesn't make sense on its own (you can't separate a regular parenthesis by comma). That's why you get an error message.
This is consistent with other optional parenthesis usage in Elixir. Try those:

    float -4 + 5
    float-4+5

A space after the function name always mean the start of the first argument. So the first example returns 1.0 and the second one fails, because it considers you are calling float(), with no arguments.

In any case, I believe the error message could be better.

José Valim
Skype: jv.ptec
Founder and Lead Developer


José Valim

unread,
May 21, 2013, 5:54:42 AM5/21/13
to elixir-l...@googlegroups.com
I have improved the error message on master:

$ elixir -e "IO.inspect (Range[], raw: true)"
** (SyntaxError) nofile:1: invalid comma inside parenthesis. If you are making a function call, do not insert spaces in between the function name and the opening parentheses. Syntax error before: )

And I want to ask everyone a favor: Every time you get a syntax error and it is not obvious what happened or if you believe the error message could be better, please do open an issue!

José Valim
Skype: jv.ptec
Founder and Lead Developer


Reply all
Reply to author
Forward
0 new messages