seeking clarity on parentheses (and commas)

700 views
Skip to first unread message

Simon St.Laurent

unread,
Apr 23, 2013, 6:49:48 AM4/23/13
to elixir-l...@googlegroups.com
I'm having a difficult time explaning Elixir syntax to beginners.  Erlang syntax might have been odd, but it was extremely regular, while Elixir offers more convenience at the price of occasional confusion.  I doubt experts and those deep in Ruby idioms encounter the confusion, but for newcomers (including me, with substantial past Ruby experience), it's not clear.

The basic rule of parentheses in Elixir seems to be that they aren't required, except when they are.  Calling functions (at http://elixir-lang.org/crash-course.html) puts it as "Elixir allows you to omit parentheses in function calls, Erlang does not."  Section 2.7, Blocks, (at http://elixir-lang.org/getting_started/2.html) shows a case where ambiguity can creep into a statement.  It also shows a comma after "if true", which I like, but which I suspect is also optional. The docs also aren't clear on where you _can_ use parentheses.  Function calls and blocks?

I'm torn between writing a book which always uses the parentheses, because they generally add "what's going on here" clarity for beginners, and a book which uses them as rarely as possible, because the use of parentheses seems to be a marker for "a newbie wrote this."  (At least it frequently runs that way in Ruby culture.)

Ideally I'd just include rules for using them - beginners seem to love rules - but those don't seem to exist in the current Elixir documentation, and I don't think I can extract them from the source code.

All suggestions welcome.

Thanks,
Simon

Alexei Sholik

unread,
Apr 23, 2013, 7:26:58 AM4/23/13
to elixir-lang-core
I think this particular example with `if` is a special case, rarely encountered in day-to-day use.
    iex> if true, do: (
    ...>   a = 1 + 2
    ...>   a + 10
    ...> )
To me, the discussion around omitting parens from function calls is an unfortunate side-effect of the implementation of Elixir's sugar for blocks. Strictly speaking, `if` is a macro and every time you write

    if true do
      x
    else
      y
    end

this is just a sugar for the call:

    if(true, [do: x, else: y])

By adding a bunch of keywords like `do`, `else`, Elixir makes it possible to write macros that look like built-in syntax. But they're still macros and can be called the usual way.

So I'm preferring to stay conservative and declare that the sole goal of introducing optional parens into the language is to allow syntactic sugar for macros. Functions are still functions and they look better with the traditional `call()` syntax.

That said, I tend to omit parens in fully qualified function calls without arguments (like `Kernel.self`) because it can't be mixed up with a variable in this case. I also usually call `IO`'s functions without parens, so that they look more like print statements than function calls.

This just doesn't look like a function call to me, more like a list of terms with missing comma after `Enum.map`:

    Enum.map xs, 0, fn (x) ->
      ...
    end

while this one's intent is clearer:

    Enum.map(xs, 0, fn (x) ->
      ...
    end)




--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Best regards
Alexei Sholik

Oren Ben-Kiki

unread,
Apr 23, 2013, 7:27:15 AM4/23/13
to elixir-l...@googlegroups.com
The syntax needs some tender loving care :-) It is a bunch of small issues rather than a single big one, though.

The `,` after an `if` is required if you write `if foo, do: something` and is forbidden if you write `if foo do\n...something...\nend`. I understand why this is, but it isn't the clearest thing to a newcomer. Then, it is OK to add line breaks in the 1st case, but they are mandatory in the 2nd case. One can't write `if coming then "Hello" else "Goodbye" end`.

Another not so favorite gotcha: `[ foo: bar ]` is fine and means [ { :foo, bar } ]`. Also `[ foo, bar | baz ]` is fine and is the same as `[ foo, bar ] ++ baz`, except that its cleaner and more efficient. But try to write `[ foo: bar | baz ]` and you get "undefined function: IEX.Helpers.|/2".

The `?` is allowed in some identifiers but not others. It is OK in functions but not variables, even though Elixir treats functions with zero arguments and variables as the same, pretty much everywhere else - so there's no conceivable example where it can cause an ambiguity problem. This makes for interesting restrictions when using function-generating macros which get updated later to also generating variables (quick, without checking, is it OK to use a final `?` in a record field name? :-) To be fair this is inherited in Ruby, which had some issue with ambiguity due to the ternary operator (which Elixir doesn't have).

I expect other people have noticed other such rough spots in the syntax... Even just listing them may be useful.

Oren Ben-Kiki


On Tue, Apr 23, 2013 at 1:49 PM, Simon St.Laurent <simo...@gmail.com> wrote:

--

Saša Jurić

unread,
Apr 23, 2013, 8:21:40 AM4/23/13
to elixir-l...@googlegroups.com
Excellent guidelines! I also tend to use this approach, although I didn't premeditate it. It came to me instinctively, probably owing to previous experiences (C++ / C# / Ruby).

Yurii Rashkovskii

unread,
Apr 23, 2013, 9:25:49 AM4/23/13
to elixir-l...@googlegroups.com
I am not sure, but `[ foo: bar | baz ]` might be an edge case worth fixing. José?

José Valim

unread,
Apr 23, 2013, 9:48:51 AM4/23/13
to elixir-l...@googlegroups.com
The `,` after an `if` is required if you write `if foo, do: something` and is forbidden if you write `if foo do\n...something...\nend`. I understand why this is, but it isn't the clearest thing to a newcomer. Then, it is OK to add line breaks in the 1st case, but they are mandatory in the 2nd case. One can't write `if coming then "Hello" else "Goodbye" end`.

I believe this should work: if coming do "Hello" else "Goodbye" end

So it looks like a bug. :)

 

Another not so favorite gotcha: `[ foo: bar ]` is fine and means [ { :foo, bar } ]`. Also `[ foo, bar | baz ]` is fine and is the same as `[ foo, bar ] ++ baz`, except that its cleaner and more efficient. But try to write `[ foo: bar | baz ]` and you get "undefined function: IEX.Helpers.|/2".

I checked with Matz (Ruby's creator), the reason for not allowing ? in variables is purely semantic, it is not related to ambiguity in the ternary. After all, Ruby also has ?a which would be equally ambiguous if you squash things together, removing the space.
 
I expect other people have noticed other such rough spots in the syntax... Even just listing them may be useful.

I know a couple more. :) One comes with the ambiguity in between unary and binary operators:

    foo+1
    foo +1
    foo + 1

What are the results you get here? :)

However, this is not something new, most languages require spaces at some places for disambiguation. For example, X div Y in erlang is different than XdivY.

Jeremy Huffman

unread,
Apr 23, 2013, 9:49:25 AM4/23/13
to elixir-l...@googlegroups.com


> I'm torn between writing a book which always uses the parentheses, because they generally add "what's going on here" clarity for beginners, and a book which uses them as rarely as possible, because the use of parentheses seems to be a marker for "a newbie wrote this."  (At least it frequently runs that way in Ruby culture.)

This really is not correct - in Ruby parenthesis are idiomatic for  method calls unless the method is a part of a DSL or is an attribute accessor as well as a few other circumstances. https://github.com/bbatsov/ruby-style-guide

What I have observed in most Elixir code out there is that people nearly always use parenthesis when calling functions, although most of the examples in the standard library do not use them. I'm not sure why the docs look like that to be honest.

Considering Elixir mostly draws on syntax and conventions from Ruby and Erlang, probably we can expect that trend to continue. Most other functional languages have different conventions though. The ML-family languages like Haskell, OCaml, F# do not use parenthesis in this way, and in Scala where they are optional you see a mix of styles but it seems more idiomatic not to use parens unless they are really needed for precedence or clarity. While LISP expressions are always in parens the function call and arguments are delimited only by whitespace.

José Valim

unread,
Apr 23, 2013, 9:57:07 AM4/23/13
to elixir-l...@googlegroups.com
I think Alexei nailed it. Today, I mostly see optional parenthesis as a mechanism to not make macros extremely verbose. So if you need a guideline to follow, I would say: skip parenthesis when you have do/end blocks or when calling one of the directives (use/import/require/alias), add parenthesis to everything else. :)

In Ruby there are many guidelines, people have different approaches when it comes to parenthesis. I don't have any. That's why the docs on stdlib are inconsistent, as Jeremy pointed out. If someone wants to work on those docs and make them abide the rule above, I wouldn't mind the consistency.

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

José Valim

unread,
Apr 23, 2013, 9:58:31 AM4/23/13
to elixir-l...@googlegroups.com
Sorry, my comments were out of place.
Both "if true do this else that end" and [foo: bar | baz] are bugs.
Matz' comment applies to supporting ? in variables or not.



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


Oren Ben-Kiki

unread,
Apr 23, 2013, 10:08:55 AM4/23/13
to elixir-l...@googlegroups.com
Ok, I opened an issues for the one-line `if` and for the `[ foo: bar | baz ]` cases.

As for the `?`, can you say a bit more about it? What is the semantic difference between a boolean function/0 and a boolean variable? That is, between:

```
defp foo?, do: function?(...)

if foo?, do: ...
```

And:

```
foo? = function?(...)

if foo?, do: ...
```

Thanks,

Oren Ben-Kiki

José Valim

unread,
Apr 23, 2013, 12:08:52 PM4/23/13
to elixir-l...@googlegroups.com
In Ruby there are many guidelines, people have different approaches when it comes to parenthesis. I don't have any. That's why the docs on stdlib are inconsistent, as Jeremy pointed out. If someone wants to work on those docs and make them abide the rule above, I wouldn't mind the consistency.

This was poorly phrased, sorry. Let me rephrase it.

We don't have consistency in our docs today and this may be cause for confusion. I believe making the docs abide to a specific rule will be positive for everyone using Elixir, including newcomers. If this is a concern writers have when writing books, it is a concern we should have when writing docs. A pull request would be really, really welcome.


Jeremy Huffman

unread,
Apr 23, 2013, 12:12:51 PM4/23/13
to elixir-l...@googlegroups.com

I will be happy to submit a PR for this.

jer...@jeremyhuffman.com

unread,
Apr 23, 2013, 11:14:23 PM4/23/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br
I submitted a PR for this. I may have missed some cases if they didn't use the iex> tag for Doctests. Doctests were very helpful for this task, thanks again Yurii!

Simon St.Laurent

unread,
Apr 24, 2013, 5:53:23 AM4/24/13
to elixir-l...@googlegroups.com, jose....@plataformatec.com.br
Thank you all - this has been a stunning response to what I feared was a stupid question.  I think this will make life much clearer for Elixir newcomers.

Thanks,
Simon
Reply all
Reply to author
Forward
0 new messages