Default argument values
On Feb 27, 2008, at 5:22 PM, Steven Mascaro wrote:
Anyway, I'm sure you know the advantages (and disadvantages?) to optional named arguments. I was just wondering whether they had been considered for ES4, or if considered and rejected, then why. I've searched the wiki and mailing list, but couldn't find anything. I also remember some brief comments on it a short while ago, but can't find them now.
I search like so:
www.google.com/search?hl=en&q=site%3Amail.mozilla.org+es4- discuss+%22named+parameters%22+brendan
and found a thread where I sounded off on named parameters:
mail.mozilla.org/pipermail/es4-discuss/2007-March/000548.html
Already you see people burning an object per call to simulate named
parameters:
foo({option1: "on", crud2: "dunno", frob: 42})
With destructuring parameters and unambiguous callsites, the object
can be optimized away. There are other ways to optimize such objects
away, for the sake of ES3 code. Point is we have never mustered a
named parameter proposal in the face of existing practice that makes
do without, and new forms such as destructuring parameters:
function foo({option1, crud2, frob}) { ... }
that reduce the pressure for named parameters, and have their own
wins and motivations aside from named parameter ones.
This is not to knock named parameters, just to explain why they never
made it into a serious proposal in the modern ES4 era.
2008/2/28 Brendan Eich <brendan at mozilla.org>:
I search like so:
www.google.com/search?hl=en&q=site%3Amail.mozilla.org+es4- discuss+%22named+parameters%22+brendan
Ah. I kept searching for variants of 'default parameters'.
This is not to knock named parameters, just to explain why they never made it into a serious proposal in the modern ES4 era.
That sounds fine. The only thing it misses is interchanging positional and named parameters, but that's no big deal. Will default values work by doing the following?
function foo({option1, crud2, frob} = {option1: 1, crud2: "dirty", frob: "enius"}) { ... }
A bit cumbersome, but otherwise OK. I tried in the RI, but it throws a ParseError. Though both the following give the same error as well:
function foo({option1, curd2, frob}) {} ({x, y} = {x: 42, y: 37})
On Feb 27, 2008, at 6:35 PM, Steven Mascaro wrote:
This is not to knock named parameters, just to explain why they
never made it into a serious proposal in the modern ES4 era.That sounds fine. The only thing it misses is interchanging positional and named parameters, but that's no big deal. Will default values work by doing the following?
function foo({option1, crud2, frob} = {option1: 1, crud2: "dirty", frob: "enius"}) { ... }
That should work.
A bit cumbersome, but otherwise OK. I tried in the RI, but it throws a ParseError. Though both the following give the same error as well:
function foo({option1, curd2, frob}) {} ({x, y} = {x: 42, y: 37})
That object destructuring shorthand was approved at
bugs.ecmascript.org/ticket/111
but I don't see a ticket, or morph of 111, asking for RI
implementation. It's not yet implemented:
function f({p:x,q:y}) [x,y]; f({p:1,q:2})
1,2
function g({p,q}) [p,q] ERROR ParseError: unknown token in destructuringField (near <no
filename>:1:1-1.8)
but as shown above, the longhand form works, and so do default
parameters:
function h({p:x,q:y} = {p:3,q:4}) [x,y] h()
3,4
On Feb 27, 2008, at 6:56 PM, Steven Mascaro wrote:
2008/2/28 Brendan Eich <brendan at mozilla.org>:
function h({p:x,q:y} = {p:3,q:4}) [x,y] h() 3,4
Unfortunately, it doesn't work when you want to specify a subset of the optional parameters. e.g.:
h({p:1}) 1,
True -- it's not the same as named parameters, for sure.
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Brendan Eich Sent: 28. februar 2008 03:47 To: Steven Mascaro Cc: es4-discuss at mozilla.org Subject: Re: Default argument values
On Feb 27, 2008, at 6:35 PM, Steven Mascaro wrote:
This is not to knock named parameters, just to explain why they never made it into a serious proposal in the modern ES4 era.
That sounds fine. The only thing it misses is interchanging
positional
and named parameters, but that's no big deal. Will default values
work
by doing the following?
function foo({option1, crud2, frob} = {option1: 1, crud2: "dirty", frob: "enius"}) { ... }
That should work.
It's cute, but there's nothing in our language discussions so far that indicates that it should work to provide defaults where the caller provided none, and making it work is pretty much tantamount to getting into a discussion about named parameters or a more general notion of destructuring where it picks up values where there are none. (At the risk of starting another "goto" discussion, imagine an operator "?=".)
In fact the RI doesn't work as one would like it to, as the third test below shows:
function h({x:x, y:y} = { x:10, y:20 }) [x,y] h({x:1,y:2})
1,2
h()
10,20
h({x:1})
1,
What appears to happen here is what one would expect: if the entire structure argument is omitted then the default value is used, but if the structure is partly omitted then the default structure is not consulted at all, and I think that's what Steven would like to see happen.
2008/2/29 Brendan Eich <brendan at mozilla.org>:
On Feb 28, 2008, at 7:29 AM, Lars Hansen wrote:
In fact the RI doesn't work as one would like it to, as the third test below shows:
function h({x:x, y:y} = { x:10, y:20 }) [x,y] h({x:1,y:2}) 1,2
h() 10,20
h({x:1}) 1,
What appears to happen here is what one would expect: if the entire structure argument is omitted then the default value is used, but if the structure is partly omitted then the default structure is not consulted at all, and I think that's what Steven would like to see happen.
Confusion: Steven, do you want to see ES4 do what the RI is doing? I thought you were looking for "member-wise" defaulting, so that the last call, to h({x:1}), would return [1, 20].
My initial concern was 'native' support for optional named arguments. In the absence of that, I'd indeed like 'member-wise' defaulting. However, I'm not sure how this affects the language.
Firstly, I can see 2 interpretations of how default values would work from the end-programmer's perspective:
Step 1. SET arg = default value Step 2. SET arg = specified value
or:
Step 1. IF no specified value, THEN SET arg = default value
Secondly, destructuring currently works as follows:
({x:a,y:b} = {x:1,y:2}); [a,b]
1,2
({x:a,y:b} = {x:1}); [a,b]
1,
typeof(b)
undefined
Suppose we took the first interpretation of default values and object destructuring were changed so that omitted (or undefined) members weren't assigned at all. Only then would a result in which calling h({x:1}) returns [1, 20] be consistent with the rest of the language. A third interpretation would involve treating destructuring defaults differently to other defaults, but I don't like that kind of special casing (from an end-programmer's perspective).
As mentioned, an alternative is an operator for defaults in destructuring. Lars, I assume you meant by this the following (in shorthand):
function h({x ?= 10, y ?= 20}) [x,y]
or in longhand in a statement:
({x:x ?= 10, y:y ?= 20} = {x: 1})
Of the alternatives, I'd probably be happiest with this kind of operator.
-----Original Message----- From: voracity at gmail.com [mailto:voracity at gmail.com] On Behalf Of Steven Mascaro Sent: 29. februar 2008 01:08 To: Brendan Eich Cc: Lars Hansen; es4-discuss at mozilla.org Subject: Re: Default argument values
Suppose we took the first interpretation of default values and object destructuring were changed so that omitted (or undefined) members weren't assigned at all. Only then would a result in which calling h({x:1}) returns [1, 20] be consistent with the rest of the language. A third interpretation would involve treating destructuring defaults differently to other defaults, but I don't like that kind of special casing (from an end-programmer's perspective).
As mentioned, an alternative is an operator for defaults in destructuring. Lars, I assume you meant by this the following (in shorthand):
function h({x ?= 10, y ?= 20}) [x,y]
or in longhand in a statement:
({x:x ?= 10, y:y ?= 20} = {x: 1})
What I meant was an assignment operator so that I could write:
{ xProp: x, yProp: y } ?= { xProp: 10, yProp: 20 };
and x and y would be overwritten only if their values were undefined. C# has a similar operator for conditional assignment which assigns if the lhs value is null (??= or something like that, I'm offline and away from my bookshelf right now).
How this fits into default arguments I don't know for sure. I superficially imagined that ?= would replace = in the parameter list:
function f({ xProp: x, yProp:y } ?= { xProp: 10, yProp: 20 }) ...
The meaning of that should be intuitive and requires fairly lightweight changes only.
Lest I get myself into another "goto" problem, I'll make it clear that I'm not advocating this for ES4. We're IMO past all deadlines for new design. I'm just thinking out loud, for the benefit of ES5 :-)
On 29/02/2008, Lars Hansen <lhansen at adobe.com> wrote:
What I meant was an assignment operator so that I could write:
{ xProp: x, yProp: y } ?= { xProp: 10, yProp: 20 };
and x and y would be overwritten only if their values were undefined. C# has a similar operator for conditional assignment which assigns if the lhs value is null (??= or something like that, I'm offline and away from my bookshelf right now).
How this fits into default arguments I don't know for sure. I superficially imagined that ?= would replace = in the parameter list:
function f({ xProp: x, yProp:y } ?= { xProp: 10, yProp: 20 }) ...
The meaning of that should be intuitive and requires fairly lightweight changes only.
Lest I get myself into another "goto" problem, I'll make it clear that I'm not advocating this for ES4. We're IMO past all deadlines for new design. I'm just thinking out loud, for the benefit of ES5 :-)
Fair enough. Although I also hope optional named arguments will make an appearance in ES5. (They're quite a priority for me personally.)
One last issue. I was going to leave it till later, but I realised it may affect ES4.
The nicest syntax for named arguments would be to use ':', just like with object literals. e.g.:
/// Define function foo(arg1 = 0, arg2 = 1) { ... }
/// Call foo(arg2: 10, arg1: 5);
(I find this even more attractive than the Python syntax.) But this could potentially conflict with the type annotation syntax. e.g.:
/// Call foo(arg1: ClassA, arg2);
At the moment, the RI throws a ParseError for that, so there's no conflict. I'd like to request that this syntax be reserved solely for named arguments.
Steven Mascaro wrote:
One last issue. I was going to leave it till later, but I realised it may affect ES4.
The nicest syntax for named arguments would be to use ':', just like with object literals. e.g.:
/// Define function foo(arg1 = 0, arg2 = 1) { ... }
/// Call foo(arg2: 10, arg1: 5);
(I find this even more attractive than the Python syntax.) But this could potentially conflict with the type annotation syntax. e.g.:
/// Call foo(arg1: ClassA, arg2);
At the moment, the RI throws a ParseError for that, so there's no conflict. I'd like to request that this syntax be reserved solely for named arguments.
Fair enough. I don't see any issues with preserving that call syntax for the future.
Waldemar
What is the opinion on Python-style named arguments? i.e.:
def f(x = 0, y = 0): ...
f(y = 2)
The calling syntax for ES4 would obviously have to be different (is a ':=' operator too incongruous in ES4?).
I find I rarely use unnamed optional arguments, because once you have more than 1, they become very awkward to deal with. In the past, I invariably found myself writing code like this (in PHP):
function f($a, $b = null, $c = null, $d = D_DEFAULT) { if ($b==null) { $b = B_DEFAULT; } if ($c==null) { $c = C_DEFAULT; } }
I would then call the function this way:
f(A_VALUE, null, null, D_VALUE);
Which would get very confusing with a large number of optional arguments:
f(A_VALUE, null, null, null, null, null, null, VALUE);
Now instead I try to write functions that take a php array for optional arguments:
function f($a, $opts = array(), $b = B_DEFAULT, $c = C_DEFAULT, $d = D_DEFAULT) { extract($opts); }
and call it like this:
f(A_VALUE, array("d" => D_VALUE));
The Python syntax is obviously far more elegant in both cases (and, I assume, would be faster to run as it would have no extra runtime overhead over positional arguments).
Besides syntactic simplicity and elegance, I also use named arguments for 'self-documenting' code (both when arguments are mandatory and optional). For example, I might write things like this in Python if I feel I need to make things clear:
drawBox(x = 0, y = 0, width = 45, height = 70) copy(source = fileA, dest = fileB)
Anyway, I'm sure you know the advantages (and disadvantages?) to optional named arguments. I was just wondering whether they had been considered for ES4, or if considered and rejected, then why. I've searched the wiki and mailing list, but couldn't find anything. I also remember some brief comments on it a short while ago, but can't find them now.