Proposal: Trailing comma in function calls

classic Classic list List threaded Threaded
29 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Proposal: Trailing comma in function calls

Axel Kittenberger
Lua accepts since ever (as far I know) trailing commas in table definitions.

And this is good, since like most people experience sooner or later, when doing multilined stuff taking care of the last element is kinda a hassle.

However, I never understood, why trailing commas are not allowed in function calls.

Reasons are the same, sometimes a function call spawns several lines. This happens for me especially with functions taking variable length arguments like print() or write()

For example:

print(
    'this is a string',
    'another string',
    'yet another string', -- note the comma here
)

I don't see any ambiguity added by this, or is there?

Patch is simple, maybe this one is a little naive, since explist() is called on various places. But works as far I tested* 

--- lua-5.2.3/src/lparser.c 2013-04-12 20:48:47.000000000 +0200
+++ lua-5.2.3-comma/src/lparser.c 2014-06-04 16:24:35.958824924 +0200
@@ -812,6 +812,7 @@
   int n = 1;  /* at least one expression */
   expr(ls, v);
   while (testnext(ls, ',')) {
+    if( ls->t.token == ')' ) break;
     luaK_exp2nextreg(ls->fs, v);
     expr(ls, v);
     n++;

Kind regards, Axel

* test suite for available for 5.2.2 hangs my computer with extreme memory usage at the same position "testing strings and string library" with or without patch, dunno if my system is just too weak for it, I too impatient or I'm doing something wrong.
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Soni "They/Them" L.

On 04/06/2014 11:48, Axel Kittenberger wrote:
Lua accepts since ever (as far I know) trailing commas in table definitions.

And this is good, since like most people experience sooner or later, when doing multilined stuff taking care of the last element is kinda a hassle.

However, I never understood, why trailing commas are not allowed in function calls.

Reasons are the same, sometimes a function call spawns several lines. This happens for me especially with functions taking variable length arguments like print() or write()

For example:

print(
    'this is a string',
    'another string',
    'yet another string', -- note the comma here
)

I don't see any ambiguity added by this, or is there?

Patch is simple, maybe this one is a little naive, since explist() is called on various places. But works as far I tested* 

--- lua-5.2.3/src/lparser.c 2013-04-12 20:48:47.000000000 +0200
+++ lua-5.2.3-comma/src/lparser.c 2014-06-04 16:24:35.958824924 +0200
@@ -812,6 +812,7 @@
   int n = 1;  /* at least one expression */
   expr(ls, v);
   while (testnext(ls, ',')) {
+    if( ls->t.token == ')' ) break;
     luaK_exp2nextreg(ls->fs, v);
     expr(ls, v);
     n++;

Kind regards, Axel

* test suite for available for 5.2.2 hangs my computer with extreme memory usage at the same position "testing strings and string library" with or without patch, dunno if my system is just too weak for it, I too impatient or I'm doing something wrong.
That just drops the comma no? I think something like "print(somemultireturnfunc(),)" should do the same as "print((somemultireturnfunc()))" instead...
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Javier Guerra Giraldez
In reply to this post by Axel Kittenberger
On Wed, Jun 4, 2014 at 9:48 AM, Axel Kittenberger <[hidden email]> wrote:
> However, I never understood, why trailing commas are not allowed in function
> calls.


I'd like to see that too.

(or to know if there's any reason behind it)

--
Javier

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Hisham
In reply to this post by Soni "They/Them" L.
On 4 June 2014 11:55, Thiago L. <[hidden email]> wrote:
> That just drops the comma no? I think something like
> "print(somemultireturnfunc(),)" should do the same as
> "print((somemultireturnfunc()))" instead...

The presence of an extra comma would leave me second-guessing the
behavior on multiple returns too. In any case, it should do the same
as the table constructor. Went to check what the table constructor
does:

Lua 5.1.2  Copyright (C) 1994-2007 Lua.org, PUC-Rio
> function f() return 1, 2 end
> t = { f() }
> print(t[1], t[2])
1    2
> u = { f(), }
> print(u[1], u[2])
1    2
> v = { f(), 3 }
> print(v[1], v[2])
1    3
>

So, yes, if function calls were to accept a trailing comma, I think
they should just drop the comma and not impose special behavior on
multiple returns, for consistency with the table constructor.

-- Hisham

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Dirk Laurie-2
In reply to this post by Axel Kittenberger
2014-06-04 16:48 GMT+02:00 Axel Kittenberger <[hidden email]>:

> Lua accepts since ever (as far I know) trailing commas in table definitions.
>
> And this is good, since like most people experience sooner or later, when
> doing multilined stuff taking care of the last element is kinda a hassle.
>
> However, I never understood, why trailing commas are not allowed in function
> calls.
>
> Reasons are the same, sometimes a function call spawns several lines. This
> happens for me especially with functions taking variable length arguments
> like print() or write()
>
> For example:
>
> print(
>     'this is a string',
>     'another string',
>     'yet another string', -- note the comma here
> )
>
> I don't see any ambiguity added by this, or is there?

More generally, "lexical void" inside an argument list could mean nil.
That is something that I (when in a bikeshedding mood) have sometimes
missed, with some library functions where I use seldom use the early
arguments, e.g.

load(source,,,MY_ENV)

But now, in your example, I don't suppose you want

print(
     'this is a string',
     'another string',
     'yet another string', -- note the comma here
)

to mean

print('this is a string', 'another string', 'yet another string', nil )

or would you?

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Soni "They/Them" L.
In reply to this post by Hisham

On 04/06/2014 12:13, Hisham wrote:

> On 4 June 2014 11:55, Thiago L. <[hidden email]> wrote:
>> That just drops the comma no? I think something like
>> "print(somemultireturnfunc(),)" should do the same as
>> "print((somemultireturnfunc()))" instead...
> The presence of an extra comma would leave me second-guessing the
> behavior on multiple returns too. In any case, it should do the same
> as the table constructor. Went to check what the table constructor
> does:
>
> Lua 5.1.2  Copyright (C) 1994-2007 Lua.org, PUC-Rio
>> function f() return 1, 2 end
>> t = { f() }
>> print(t[1], t[2])
> 1    2
>> u = { f(), }
>> print(u[1], u[2])
> 1    2
>> v = { f(), 3 }
>> print(v[1], v[2])
> 1    3
> So, yes, if function calls were to accept a trailing comma, I think
> they should just drop the comma and not impose special behavior on
> multiple returns, for consistency with the table constructor.
>
> -- Hisham
>
But code golfing... :(

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Luiz Henrique de Figueiredo
In reply to this post by Axel Kittenberger
> However, I never understood, why trailing commas are not allowed in
> function calls.

One argument for not supporting this is that argument lists are almost never
generated automatically, which was the original motivation for supporting
trailing commas in table constructors, as this simplifies code generation.

Another is that argument lists in practice are never very long.

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Andrew Starks



On Wed, Jun 4, 2014 at 11:15 AM, Luiz Henrique de Figueiredo <[hidden email]> wrote:
> However, I never understood, why trailing commas are not allowed in
> function calls.

One argument for not supporting this is that argument lists are almost never
generated automatically, which was the original motivation for supporting
trailing commas in table constructors, as this simplifies code generation.

Another is that argument lists in practice are never very long.


Tables don't store nil values, so there is no ambiguity there.

With argument lists, it would be ambiguous as to whether or not a nil was being placed there and that is not the same thing.

local function foo(...)
   return select("#", ...)
end

assert( foo() < foo(nil))

-Andrew

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Axel Kittenberger
Andrew> With argument lists, it would be ambiguous as to whether or not a nil was being placed there and that is not the same thing.

There is no implicit nil beyond the last comma, by definition. It was never valid code before, so there is no breakage. What Dirk posted before as lexical void meaning nil, was a suggestion. I'm not hot for that, since I don't think it helps readability, but I wouldn't mind either if others like it. 

Luiz> ... as this simplifies code generation.

I don't know what people are autogenerating, but why wouldn't they autogenerate function calls when generating code, but only tables? I at least do the former :-) But honestly I never cared so much about that, the extra if to prepend a comma in the output loop when index is greater than 1 never was that much a hassle for me. Its more hand written code where I don't like to have failing code if forgetting to remove the trailing comma when removing what was previously the last entry.

Luiz> Another is that argument lists in practice are never very long.

Well there is only one trailing comma independently if the list was short or long. Its always one that is or isn't an issue. But I guess you mean cases where going multilined. write() is a case where it happens often, especially in luatex I experienced long argument lists with write. And there maintaining the last comma there or not there by hand is cumbersome.

Anyway, its all not too important, going to live a happy life even without trailing commas in functions :-) Just thought this would be something rather easy to fix. I also don't know of any other language that supports this, but Lua was cooler than most others anyway.

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Andrew Starks

On Wed, Jun 4, 2014 at 12:13 PM, Axel Kittenberger <[hidden email]> wrote:
There is no implicit nil beyond the last comma, by definition. It was never valid code before, so there is no breakage. What Dirk posted before as lexical void meaning nil, was a suggestion. I'm not hot for that, since I don't think it helps readability, but I wouldn't mind either if others like it. 

Cool. I don't mean to convey a desire to shoot this down. I do think that if you're looking at `foo(x, y, )`, and you don't have Lua's specification committed to memory, and you happen to care[1], then it would be a quick trip to the reference guide.

It's up to whomever to decide if that _slight_ ambiguity is worth it. I've gotten my share of  "unexpected symbol near ')" errors and would probably favor the change, if its presence meant no change to the argument count.

-Andrew 


[1] An edge case to be sure, but I have code that uses select and does about the argument count, independent of the last nil.
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Javier Guerra Giraldez
In reply to this post by Axel Kittenberger
On Wed, Jun 4, 2014 at 12:13 PM, Axel Kittenberger <[hidden email]> wrote:
> I also don't know of any other language that supports this


python does:

def s(a,b):
    return a+b

s(1,2)
=> 3

s(1,2,)
=> 3

s(1,2,3)
=> TypeError: s() takes exactly 2 arguments (3 given)



--
Javier

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Florian Weimer
In reply to this post by Luiz Henrique de Figueiredo
* Luiz Henrique de Figueiredo:

> One argument for not supporting this is that argument lists are almost never
> generated automatically, which was the original motivation for supporting
> trailing commas in table constructors, as this simplifies code generation.
>
> Another is that argument lists in practice are never very long.

And they are rarely split as one item per line.  For tables, the
trailing comma reduces visual clutter in diffs if there's one item per
line.

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Tom N Harris
In reply to this post by Dirk Laurie-2
On Wednesday, June 04, 2014 05:15:23 PM Dirk Laurie wrote:
> More generally, "lexical void" inside an argument list could mean nil.
> That is something that I (when in a bikeshedding mood) have sometimes
> missed, with some library functions where I use seldom use the early
> arguments, e.g.
>
> load(source,,,MY_ENV)

I like this. And on the left side too.

    first,,third = something()

Assigns the first and third values while dropping the second without having to
assign a dummy variable.

> But now, in your example, I don't suppose you want
> to mean
>
> print('this is a string', 'another string', 'yet another string', nil )
>
> or would you?

You can have both. The formal definition of an unnamed element in a comma list
is "nothing". That is,

    print() -- is not the same as
    print(nil) -- thus
    print(,) -- is not the same as
    print(nil,nil)

It's not that you're ignoring the last comma when there's no variable after
it, you're implying the last comma after a variable name. Then the number of
arguments to the function is equal to the number of commas.

--
tom <[hidden email]>

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Luiz Henrique de Figueiredo
> load(source,,,MY_ENV)
> first,,third = something()

The idiom in these cases would be

        load(source,_,_,MY_ENV)
        first,_,third = something()

Perhaps it'd be helpful to have a second predeclared local named "_",
as a cheap throw-away variable...

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Mike Nelson
On 6/4/2014 6:01 PM, Luiz Henrique de Figueiredo wrote:

>> load(source,,,MY_ENV)
>> first,,third = something()
> The idiom in these cases would be
>
> load(source,_,_,MY_ENV)
> first,_,third = something()
>
> Perhaps it'd be helpful to have a second predeclared local named "_",
> as a cheap throw-away variable...
>
Rather like Go, where _ is predeclared for the same purpose (would be
surprised if they didn't get the idea from Lua). What does everyone
think about also outlawing _ as an rvalue, as Go does?
This would firmly establish _ as a throw-away variable, but is it worth
the internal complexity to enforce it? Perhaps the middle way of
predeclaring but not enforcing is best.

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Elias Barrionovo

On Jun 4, 2014 11:31 PM, "Mike Nelson" <[hidden email]> wrote:
> What does everyone think about also outlawing _ as an rvalue, as Go does?

It would go against the "mechanisms, not policies" mantra of the language.

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Coda Highland
In reply to this post by Mike Nelson
On Wed, Jun 4, 2014 at 7:30 PM, Mike Nelson <[hidden email]> wrote:

> On 6/4/2014 6:01 PM, Luiz Henrique de Figueiredo wrote:
>>>
>>>         load(source,,,MY_ENV)
>>>         first,,third = something()
>>
>> The idiom in these cases would be
>>
>>         load(source,_,_,MY_ENV)
>>         first,_,third = something()
>>
>> Perhaps it'd be helpful to have a second predeclared local named "_",
>> as a cheap throw-away variable...
>>
> Rather like Go, where _ is predeclared for the same purpose (would be
> surprised if they didn't get the idea from Lua). What does everyone think
> about also outlawing _ as an rvalue, as Go does?
> This would firmly establish _ as a throw-away variable, but is it worth the
> internal complexity to enforce it? Perhaps the middle way of predeclaring
> but not enforcing is best.

The first of Luiz's examples uses _ as an rvalue.

/s/ Adam

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Philipp Janda
Am 05.06.2014 05:01 schröbte Coda Highland:

> On Wed, Jun 4, 2014 at 7:30 PM, Mike Nelson <[hidden email]> wrote:
>> On 6/4/2014 6:01 PM, Luiz Henrique de Figueiredo wrote:
>>>>
>>>>          load(source,,,MY_ENV)
>>>>          first,,third = something()
>>>
>>> The idiom in these cases would be
>>>
>>>          load(source,_,_,MY_ENV)
>>>          first,_,third = something()
>>>
>>> Perhaps it'd be helpful to have a second predeclared local named "_",
>>> as a cheap throw-away variable...
>>>
>> Rather like Go, where _ is predeclared for the same purpose (would be
>> surprised if they didn't get the idea from Lua). What does everyone think
>> about also outlawing _ as an rvalue, as Go does?
>> This would firmly establish _ as a throw-away variable, but is it worth the
>> internal complexity to enforce it? Perhaps the middle way of predeclaring
>> but not enforcing is best.
>
> The first of Luiz's examples uses _ as an rvalue.

... and if you swap those two examples you will get weird results, so
that would actually be an argument _against_ using `_` as an rvalue (at
least for normal code).

And predeclaring is not really useful. Usually you do

     local first,_,third = something()

anyway, and if you predeclare `first` and `third`, you can add `_` to
the list there as well ...

>
> /s/ Adam
>

Philipp



Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Tom N Harris
In reply to this post by Luiz Henrique de Figueiredo
On Wednesday, June 04, 2014 10:01:13 PM Luiz Henrique de Figueiredo wrote:
> The idiom in these cases would be
>
> load(source,_,_,MY_ENV)
> first,_,third = something()
>

Of course. But if the lines were reversed you'd have a problem. I'm sure
everyone knows not to trust `_` as a rvalue and explicitly writes `nil`. If
`_` were reserved as a placeholder name then as a lvalue it's immediately
forgotten, and as a rvalue it evaluates to `nil`. But then what's the
difference between this and what Dirk and I suggested with empty items in the
comma list? It's not merely syntactic sugar because you're indicating the
value is going to be discarded immediately. The compiler then knows it doesn't
need to keep that register slot around unlike the `local _`. Not to mention
gcing the value earlier.

I'm pretty sure there are sources out there that use `_` for something other
than a placeholder lvalue. Any change to how it's handled would break
someone's code.

Alternately allow `nil` as a lvalue that acts as the bit bucket.

--
tom <[hidden email]>

Reply | Threaded
Open this post in threaded view
|

Re: Proposal: Trailing comma in function calls

Coda Highland
On Wed, Jun 4, 2014 at 9:56 PM, Tom N Harris <[hidden email]> wrote:
> Alternately allow `nil` as a lvalue that acts as the bit bucket.

I like this best.

/s/ Adam

12