Come on Elixir, if PHP can do it, anybody can - trailing comma now allowed in function & method calls


#21

Trailing commas without parenthesis will always be interpreted in the wrong way because we have no way of knowing what the last comma is. It is impossible to implement. We should also strive to reduce the amount of ambiguity in the language, introducing more feels like going in the wrong direction.


#22

Oooo, I did not know it had this inconsistency… I may need to start adding an ending optional ignored argument to all my multi-line (mostly macro) functions so I can keep , at the end. :slight_smile:

The language has a ton of ‘surprising’ cases like this already, this would actually remove one of the surprises if trailing commas were allowed inside parenthesis. :wink:


#23

This is exactly why we can’t have trailing commas. You are doing a function call without parenthesis so with trailing commas it’s ambiguous what the last argument is.

I hadn’t considered that.

However, it seems that the root cause of the ambiguity is that parentheses are optional. There’s nothing ambiguous about this, is there?

# syntax error
ArgsDemo.normal_args(
  10,
  "blue",
)

If there’s a closing parenthesis, couldn’t the parser notice that and ignore the trailing comma?

We should also strive to reduce the amount of ambiguity in the language

I totally agree. Making parentheses required for function calls would remove the ambiguity, at some (arguable) aesthetic cost.


#24

Indeed, I’d have the parser only accept trailing commas if it is inside parenthesis, otherwise it’d be disallowed.


#25

Indeed, I’d have the parser only accept trailing commas if it is inside parenthesis, otherwise it’d be disallowed.

Yes - or inside [].


#26

Yes, that’s what we do inside brackets. But we don’t want to add it for function calls because, like I said earlier, we can’t support it for parenthesis-less functions and we feel it would be inconsistent and confusing to only support it for some function calls.

That would be a breaking change and would not possible until Elixir 2.0.


#27

I’m not sure this topic is worth discussing any longer. I think everyone feels like the loss of the trailing comma is worth losing over losing optional parenthesis. I remember the backlash when elixir started warning about omitting parenthesis when piping and I’m fairly positive thats not something the community would be willing to trade.


#28

I wonder why you would want trailing commas in function calls.

Functions in Elixir and Erlang are defined by their name and arity. A trailing comma in a function call seems to introduce unnecessary ambiguity when it comes to the arity of the called function.

Does foo(bar,) refer to foo/1 or foo/2 with the second parameter accidentally omitted?


#29

Multi-line, especially useful for macro’s:

someMacro(
  ModuleName,
  a_very_long_argument_maybe_some_AST_for_the_macro,
  or_maybe - a_set_of + math_that / needs_to * be_performed,
  a: 1, # Or keyword list for those that like to use them without `[]`
  b: 2,
  c: 3,
)

Convoluted example, but it gets the point across. Plus it unifies the trailing comma syntax with [] and {} and %{} too, thus making things more consistent. :slight_smile:

I’d never recommend a trailing comma in a single-liner, not in any of those.


#30

But in this example, you’re still using the trailing comma for a keyword list and not for function parameters.


#31

Each of those lines were intended to be exampled stand-alone. ^.^


#32

I’ve always felt conflicted about the syntactic sugar that allows dropping brackets on keyword lists if they’re the final parameter of a function call. For instance, try explaining to a newcomer to Elixir why this should happen:

IO.inspect("bar", label: "foo")
# foo: "bar"
# "bar"
IO.inspect(a: "bar", label: "foo")
# [a: "bar", label: "foo"]
# [a: "bar", label: "foo"]

This bit of sugar makes reading code more confusing for a somewhat cool (but not essential, as far as I know), feature. If it were up to me, I would enforce brackets around keyword lists (and continue to push replacing keyword lists with map). So the previous calls would unambiguously be:

IO.inspect("bar", [label: "foo"])
IO.inspect([a: "bar", label: "foo"])

In any case, I lean more towards the side of keeping the syntax between function calls with and without parens as close as possible, and don’t think trailing commas inside function calls with parens moves the language in a good direction.


#33

Absolutely thus. If Elixir did not start before maps were added to the BEAM, I bet that is what would have been done.


#34

For this special case you can simply put the comma at the beginning of the line.
Elm is doing it this way and it feels not that bad to me.

%{
    one: "hello"
  , two: "goodbye"
+ , three: "we meet again"
}

#35

I prefer the OCaml way. ^.^

  %{
    one: "hello",
    two: "goodbye",
+   three: "we meet again",
  }

I.E. Trailing comma’s. ^.^
(Although technically OCaml uses ; instead of comma’s to separate list and record elements, and comma’s are used to separate tuple elements, and function arguments have no separators at all).


#36

Counterexample:

 %{
+  zero: 0
+ ,one: "hello"
  ,two: "goodbye"
 }

Anyway, this discussion is pointless, as I said earlier, since current version of the elixir formatter removes all trailing commas anyway.

There are tests in https://github.com/elixir-lang/elixir/blob/7f8136915fe249efa47a21a89ff0f04e880264fc/lib/elixir/test/elixir/code_formatter/containers_test.exs that show that the removal of trailing comma is as expected.

Also I remember a discussion on core or the issue tracker where @josevalim said, that trailing commans will be removed by the formatter and that this is not an option to discus.

So as much as I’d like to have them, in my opinion we can remove them from the parser in general, since the standard formatter, which we are all encouraged to use from 1.6s release on, will remove them anyway!


#37

Technically Elm’s style is more like:

%{one: "hello"
 ,two: "goodbye"
}

As weird as that is, which would make your counterexample even more funky:

+ %{zero: 0
+  ,one: "hello"
   ,two: "goodbye"
  }

^.^;


#38

Wonderful idea!
Let’s destroy all structure in code and make everything look like everything else!
clap


#39

Structure is not syntax, the structure would remain identical here.


#40

I’m happy to drop this issue - it’s not the way I prefer, but it’s not important.