an idea for replacing arguments.length

# Allen Wirfs-Brock (12 years ago)

One of the the few remaining uses of a function's 'arguments' binding is to determine the actual number of passed arguments. This is necessary in some overloading scenarios where a function has different behavior when an argument is completely absent then it has when undefined (or any other default value) is explicitly passed in that parameter position. That situation occurs in a number of DOM APIs and even a few ES library functions.

For example (see ecmascript#1877 ), Array.prototype.splice returns different results for:

[1,2,3].splice()

and

[1,2,3].splice(undefined)

The natural ES6 declaration for a splice function is:

function splice(start, deleteCount, ...items) {...

but if you write it this way then within the body you have to have a test like:

if (arguments.length == 0) {...

to implement the correct web-compatable behavior.

Or, alternatively you could declare the functions as:

function splice(...actualArgs) {
     let [start, stop, ...item] = actualArgs;
     ...
     if (actualArgs.length == 0) {...

So, to implement a Web-compaable version of splice you either have to use 'arguments' to determine the actual number of passed objects or you need to declare it with a bogus parameter pattern and use explicit or implicit destructuring to parse out the positional parameters.

One way around this dilemma would be to provide a syntactic affordance for determing the actual argument count. For example, one possibility would be to allow the last item of any formal parameter list to be an item of the syntactic form:

ActualArgumentCount : '#' BindingIdentifier

So, the declaration for splice could then be:

function splice(start, deleteCount, ...items, #argCount) {
   ...
   if (argCount == 0) {...

Thoughts?

# Mark S. Miller (12 years ago)
  1. "#" is one of the few ascii characters we haven't used up yet. Let's not use it up on a minor convenience.

  2. Introducing new syntax for use cases that are expected to be frequent, e.g., classes, often pays for its weight. The frequency makes it likely that readers will understand what it is the vast majority of times they encounter it. New syntax that gets used once in a blue moon can only be justified if the pain of doing without in those blue-moon cases is huge. In this case, it isn't. For those rare occasions when they see it, readers are much more likely to guess what "arguments.length" mean that this funny newfangled "#" thing.

Code is read much more often than it is written -- at least code that matters. Brevity is useful as a metric only to the degree that it serves as a proxy for cognitive load on the reader.

# Dmitry Soshnikov (12 years ago)

Interesting, unless we haven't ...rest already. Otherwise, sounds as an overhead (and really just a workaround to fix a particular issue, but not a very useful language construct) to me. Since in the ES6 world, if a function is designed to work with variant number of arguments, this should (naturally) be done via ...rest arguments.

The overhead is: (...args, #argsCount) => {}: in this case args.length

and #argsCount do the same, that increases the ways of doing two the same things with different approaches (not the best of way).

So yeah, I'd assume the second way of implementation in your example (with destructing or whatever) as a more natural and straightforward.

Dmitry

# Brendan Eich (12 years ago)

On the other hand, we are this close in ES6 to relieveing people from having to use arguments at all.

I like the intention behind Allen's idea. To defend the concrete syntax a bit, one would argue that only the final formal parameter may be prefixed with #, and we are free to use # in other ways elsewhere.

If # should be kept reserved even in this context, then perhaps something like

function f(args: x, y, z) { // use x, y, z freely (they could be patterns) switch (args.length) { case 1: ... case 2: ... // etc. } }

The "label" names a rest parameter for the entire parameter list.

# Allen Wirfs-Brock (12 years ago)

On Nov 10, 2013, at 10:52 AM, Mark S. Miller wrote:

  1. "#" is one of the few ascii characters we haven't used up yet. Let's not use it up on a minor convenience.

But this is a very restrictive context. It isn't clear that it would impact any other possible usage.

# Allen Wirfs-Brock (12 years ago)

On Nov 10, 2013, at 11:08 AM, Brendan Eich wrote:

On the other hand, we are this close in ES6 to relieveing people from having to use arguments at all.

I like the intention behind Allen's idea. To defend the concrete syntax a bit, one would argue that only the final formal parameter may be prefixed with #, and we are free to use # in other ways elsewhere.

If # should be kept reserved even in this context, then perhaps something like

The use of : suggests that another alternative could be:

 function splice(start, deleteCount, ...items, :argCount) {

but I still lie # better and don't think this usage would have to impact any other possbile use of that character.

# David Bruant (12 years ago)

Le 10/11/2013 19:12, Allen Wirfs-Brock a écrit :

One of the the few remaining uses of a function's 'arguments' binding is to determine the actual number of passed arguments. This is necessary in some overloading scenarios where a function has different behavior when an argument is completely absent then it has when undefined (or any other default value) is explicitly passed in that parameter position. That situation occurs in a number of DOM APIs and even a few ES library functions.

For example(see ecmascript#1877 ), Array.prototype.splice returns different results for: [1,2,3].splice() and [1,2,3].splice(undefined)

The natural ES6 declaration for a splice function is:

function splice(start, deleteCount, ...items) {...

but if you write it this way then within the body you have to have a test like:

if (arguments.length == 0) {...

to implement the correct web-compatable behavior.

Or, alternatively you could declare the functions as:

function splice(...actualArgs) { let [start, stop, ...item] = actualArgs; ... if (actualArgs.length == 0) {...

So, to implement a Web-compaable version of splice you either have to use 'arguments' to determine the actual number of passed objects or you need to declare it with a bogus parameter pattern and use explicit or implicit destructuring to parse out the positional parameters.

I imagine it also breaks splice.length, but that's fixed by making length configurable (writable? I don't remember). I'm fine with the second solution. It's inelegant, but it's also legacy... I don't think differenciating .splice() and .splice(undefined) and equivalent use cases is a practice that should be encouraged.

One way around this dilemma would be to provide a syntactic affordance for determing the actual argument count. For example, one possibility would be to allow the last item of any formal parameter list to be an item of the syntactic form:

ActualArgumentCount : '#' BindingIdentifier

So, the declaration for splice could then be:

function splice(start, deleteCount, ...items, #argCount) { ... if (argCount == 0) {...

Thoughts?

Why creating something new if it's only encouraging a bad practice? Is there a good use case?

# Domenic Denicola (12 years ago)

From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of David Bruant

Why creating something new if it's only encouraging a bad practice? Is there a good use case?

+1. I thought we were trying to make undefined and not passed equivalent. Adding a feature specifically designed to bypass this seems unfortunate, especially since we already have an ugly feature (arguments.length) for doing such an ugly thing.

On the other hand, Brendan's proposal seems pretty neat. I am pretty sure that while writing some Traceur code I wanted something similar. But even then, I suppose I would want args in his example to always have length 3, so as to discourage the bad practice. I.e. f(1) would give args as [1, undefined, undefined]. It would be a way of reifying "arguments for the ES6 era," where differences between not-passed and undefined are minimized, and of course it would be a real array.

# Mark S. Miller (12 years ago)

and Allen, you are both responding only to my point #1. I consider my point #2 --- cognitive load of the reader on encountering a rarely seen construct --- to be the more significant criticism here.

On these grounds, your

function f(args: x, y, z) {

is worse. Given how ":" is already used in JS, this suggests that "args" is either a label name, which the more astute reader can easily disqualify, or a property name, suggesting some shorthand for named parameters.

Note that I am not saying that the meanings of all syntactic constructs have to be guessable with no guidance. But the rarely used ones do.

  1. Awkward syntax for some rarely used cases is the price of good syntax for common cases.
# Mark S. Miller (12 years ago)

Also

function f(args: x, y, z) {

will suggest to TypeScript readers that the parameter "args" has type "x".

# Andrea Giammarchi (12 years ago)

I would rather think about a smart syntax able to tell the function it should expose arguments as it was before.

function splice:arguments(start, deleteCount, ...items)

or

function splice:args(start, deleteCount, ...items)

or anything different that looks reasonable ? Or maybe not useful at all as Dmitry said (and I agree)

# Brendan Eich (12 years ago)

Good point - between that and your previous message, I withdraw the label idea.

# Brendan Eich (12 years ago)

The other objection is that counting top level commas misleads.

# Allen Wirfs-Brock (12 years ago)

On Nov 10, 2013, at 11:44 AM, Mark S. Miller wrote:

Hi Brendan and Allen, you are both responding only to my point #1. I consider my point #2 --- cognitive load of the reader on encountering a rarely seen construct --- to be the more significant criticism here.

Hence my choice of using # which is more suggestive to the meaning. It seems suggestive enough that once somebody was initially exposed to it they would understand it when they say it.

Note that I am not saying that the meanings of all syntactic constructs have to be guessable with no guidance. But the rarely used ones do.

  1. Awkward syntax for some rarely used cases is the price of good syntax for common cases.

But sometimes syntax to provide access to basic information or functionality that is rarely used. Arguably 'arguments' already provides such a hook but we are trying to generally discourage use of 'arguments' because of its other undesirable characteristics.

A different alternative would be via a new default bind. We could specify that every function has an implicit const bind for 'actualArgCount' (unless there is a different explicit declaration for that name).

As for rarity. I've been in enough long discussions with Web API folks on the public-script-coord list and on various WebIDL bugs to feel that this usage to define over-loads isn't that rare in the DOM API world. It would be nice for ES to have a story for handling such definitions other than falling back to using 'arguments'

# Oliver Hunt (12 years ago)

I don’t fully understand this — what is the reason for needing to know actual argument count? The historical reason was that if i have a var arg function with a few required parameters i don’t have any nice way of iterating the optional args.

Default and rest parameters seem to solve this use case.

—Oliver

# Mark S. Miller (12 years ago)

Does this become common for those using DOM APIs or for those implementing DOM APIs? If only the latter, well...

I want to retire "arguments" as well. For these rare cases, I would use the patterns involving "...". Combining "..." with destructuring patterns in the body ain't too bad.

And introducing a new default binding that's implicitly in scope without explicit definition? C'mon Allen. "this", "arguments", "return", and "yield" already give programmers enough TCP refactoring hazards, such as when converting a for loop to a .forEach. How many red-flag identifiers must programmers scan for before undertaking this refactoring?

# Oliver Hunt (12 years ago)

Sorry, ignore the line noise I just remembered that we decided to treat undefined as missing.

I do recall actually making the argument w.r.t defaults, that treating undefined as “not provided” would cause us difficulty if anyone ever wanted to distinguish undefined from not provided.

—Oliver

# Allen Wirfs-Brock (12 years ago)

On Nov 10, 2013, at 12:52 PM, Oliver Hunt wrote:

I don’t fully understand this — what is the reason for needing to know actual argument count? The historical reason was that if i have a var arg function with a few required parameters i don’t have any nice way of iterating the optional args.

Default and rest parameters seem to solve this use case.

The legacy reason is that there are existing APIs that were probably originally implemented in C++ where the behavior of the function was determined by the number of passed arguments rather than by their actual values.

The continuing reason is that WebIDL overloading model encourages people to design new APIs that have that same characteristic. And so far, nobody has convinced the WebIDL maintainers to abandon such overloading as a specification technique.

# Allen Wirfs-Brock (12 years ago)

On Nov 10, 2013, at 12:53 PM, Mark S. Miller wrote:

Does this become common for those using DOM APIs or for those implementing DOM APIs? If only the latter, well...

I want to retire "arguments" as well. For these rare cases, I would use the patterns involving "...". Combining "..." with destructuring patterns in the body ain't too bad.

A problem is that the implementation signature of an API is implemented is what is often document as the as the usage signature. So, if a lot of DOM APIs need to be implemented as function (...args) {

then that is likely what will appear in documentation.

Also note that there is likely to be actual computational overhead in both creating a rest argument and in destructuring it. In some cases, that overhead may be an issue.

And introducing a new default binding that's implicitly in scope without explicit definition? C'mon Allen. "this", "arguments", "return", and "yield" already give programmers enough TCP refactoring hazards, such as when converting a for loop to a .forEach. How many red-flag identifiers must programmers scan for before undertaking this refactoring?

that's why I suggested (...,#argCount) If you don't want to use new syntax then a built-in binding is a fallback solution.

# Andrea Giammarchi (12 years ago)

fwiw, I have few APIs that use null or undefined / void 0 as explicit action to remove an item ... an example.

function cookie(key, value) {
  if (arguments.length === 1) return get(key);
  if (value == null) return del(key);
  set(key, value);
  return value;
}

Although I would use function cookie(...args){} as soon as supported and/or arguments would be undefined ... no big deal.

In the other case I would simply do something like:

function splice(a, b, ...rest) {
  var numArgs = rest.length + (b !== void 0) + (a !== void 0);
}

Not sure why this is so needed though.

# Brendan Eich (12 years ago)

On Nov 10, 2013, at 9:12 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

Not sure why this is so needed though.

Allen's posts make the case: webidl and varargs-style functions. Not all legacy.

# David Bruant (12 years ago)

Le 10/11/2013 22:19, Brendan Eich a écrit :

On Nov 10, 2013, at 9:12 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

Not sure why this is so needed though. Allen's posts make the case: webidl and varargs-style functions. Not all legacy.

WebIDL creates spec, not code. The language syntax doesn't need to evolve for that. Allen showed that rest params+destructuring allows self-hosting without |arguments| Varargs functions have rest parameters.

# K. Gadd (12 years ago)

JSIL and embind both need arguments.length for efficient method call dispatch when dealing with overloaded functions. Is it your intent that all such scenarios must now pay the cost of creating an array (to hold the rest arguments) and then destructuring it, for every call? At present it's possible to avoid this overhead in V8 and SpiderMonkey by using arguments.length + arguments[n] or by using arguments.length + patterned argument names.

# Oliver Hunt (12 years ago)

The mere fact the this discussion is coming up, is a good argument (to me anyway) that default parameter behaviour should have been in terms of argument count, not |undefined|. Again, sentinel values that are regular valid values are a bad thing.

I still cannot recall what the usage scenario that required undefined => not provided was, but this seems like a very good argument for that decision being wrong.

—Oliver

# Mark S. Miller (12 years ago)

On Sun, Nov 10, 2013 at 1:08 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Nov 10, 2013, at 12:53 PM, Mark S. Miller wrote:

Does this become common for those using DOM APIs or for those implementing DOM APIs? If only the latter, well...

I want to retire "arguments" as well. For these rare cases, I would use the patterns involving "...". Combining "..." with destructuring patterns in the body ain't too bad.

A problem is that the implementation signature of an API is implemented is what is often document as the as the usage signature. So, if a lot of DOM APIs need to be implemented as function (...args) {

then that is likely what will appear in documentation.

Generate the documentation from the WebIDL.

Also note that there is likely to be actual computational overhead in both creating a rest argument and in destructuring it. In some cases, that overhead may be an issue.

When it is a performance cost, use arguments.length. I hate it, but less than adding a new feature to the language merely to make an anti-pattern more convenient.

And introducing a new default binding that's implicitly in scope without explicit definition? C'mon Allen. "this", "arguments", "return", and "yield" already give programmers enough TCP refactoring hazards, such as when converting a for loop to a .forEach. How many red-flag identifiers must programmers scan for before undertaking this refactoring?

that's why I suggested (...,#argCount) If you don't want to use new syntax then a built-in binding is a fallback solution.

I agree that #argCount is better than introducing a new builtin binding. #argCount is also better than being hit on the head with a hammer.

# David Bruant (12 years ago)

Le 10/11/2013 22:30, K. Gadd a écrit :

JSIL and embind both need arguments.length for efficient method call dispatch when dealing with overloaded functions. Is it your intent that all such scenarios must now pay the cost of creating an array (to hold the rest arguments) and then destructuring it, for every call? At present it's possible to avoid this overhead in V8 and SpiderMonkey by using arguments.length + arguments[n] or by using arguments.length + patterned argument names.

The array created by rest arguments has no reason to cost more than the arguments object. It's only an implementation concern.

# Brendan Eich (12 years ago)

On Nov 10, 2013, at 9:24 PM, David Bruant <bruant.d at gmail.com> wrote:

WebIDL creates spec, not code. The language syntax doesn't need to evolve for that.

Wrong, webidl and jsidl (and jsil and embind) are both interface and a bit of implementation. Testing argc != testing sentinel value. Two different semantics, plausibly deserving fast and terse syntax.

Allen showed that rest params+destructuring allows self

Read Allen's replies, stop ignoring known counter-arguments.

# Mark S. Miller (12 years ago)

On Sun, Nov 10, 2013 at 1:30 PM, K. Gadd <kg at luminance.org> wrote:

JSIL and embind both need arguments.length for efficient method call dispatch when dealing with overloaded functions. Is it your intent that all such scenarios must now pay the cost of creating an array (to hold the rest arguments) and then destructuring it, for every call? At present it's possible to avoid this overhead in V8 and SpiderMonkey by using arguments.length + arguments[n] or by using arguments.length + patterned argument names.

Hi Katelyn,

No one is taking arguments away. Perhaps we would if we could but we can't. So as I said just now to Allen, if you really need to do this, go ahead and use arguments.length.

But do you really need to do this? Assuming for a moment that we were all agreed that the best practice is to treat absence the same as undefined, why not go with the best practice and be done?

# Brendan Eich (12 years ago)

On Nov 10, 2013, at 9:32 PM, Oliver Hunt <oliver at apple.com> wrote:

I still cannot recall what the usage scenario that required undefined => not provided was, but this seems like a very good argument for that decision being wrong.

C'mon, we have been over this a kajillion times. Delegation without sentinels requires explosive case analysis and call sites in the delegating functions.

Don't let the varargs but not sentinel fixable case be the tail that wags the dog. Here, we are debating fine hairs on the tail.

# Dmitry Soshnikov (12 years ago)

On Nov 10, 2013, at 1:08 PM, Allen Wirfs-Brock wrote:

On Nov 10, 2013, at 12:53 PM, Mark S. Miller wrote:

Does this become common for those using DOM APIs or for those implementing DOM APIs? If only the latter, well...

I want to retire "arguments" as well. For these rare cases, I would use the patterns involving "...". Combining "..." with destructuring patterns in the body ain't too bad.

A problem is that the implementation signature of an API is implemented is what is often document as the as the usage signature. So, if a lot of DOM APIs need to be implemented as function (...args) {

then that is likely what will appear in documentation.

Also note that there is likely to be actual computational overhead in both creating a rest argument and in destructuring it. In some cases, that overhead may be an issue.

I think I'm with Mark here, and as mentioned, don't think spreading two equivalent ways of doing the same thing (args.length and #count in case when have only the ...args) is the best design decision.

Yes, it's true that destructuring could make some overhead, but no one is tight to use it: a var-args function still can be described as ...args only and check its length as it does with the arguments.

Moreover, for this particular splice example, I don't think the (start, deleteCount, ...rest) is the best signature (not to say, incorrect signature). As again was mentioned, a var-args function seems should just use the ...rest params, and exactly starting from the position when the first optional argument is started. And if it's started right from the position 0 (as with the splice), then probably the more natural signature would be the (...args).

P.S.: I do also agree that it's better to reserve the syntax of (foo: bar) for the optional types (that are used in TypeScript now).

Dmitry

# David Bruant (12 years ago)

Le 10/11/2013 22:42, Brendan Eich a écrit :

On Nov 10, 2013, at 9:24 PM, David Bruant <bruant.d at gmail.com> wrote:

WebIDL creates spec, not code. The language syntax doesn't need to evolve for that. Wrong, webidl and jsidl (and jsil and embind) are both interface and a bit of implementation. Testing argc != testing sentinel value. Two different semantics, plausibly deserving fast and terse syntax.

One of the semantics is admitted as a bad practice. I still don't understand why it should be encouraged. Other use cases are compile-to-JS use case. Can implementations optimize the pattern Allen showed in his initial post? function splice(...actualArgs) { let [start, stop, ...item] = actualArgs; ... if (actualArgs.length == 0) {...

Allen showed that rest params+destructuring allows self Read Allen's replies, stop ignoring known counter-arguments.

Not my intention, sorry it came out this way, I had missed a few posts.

Allen wrote:

So, if a lot of DOM APIs need to be implemented as function (...args) {

then that is likely what will appear in documentation.

As Mark said, generate doc from WebIDL. Else, MDN and WPD are CC-licenced wikis :-)

Also note that there is likely to be actual computational overhead in both creating a rest argument and in destructuring it. In some cases, that overhead may be an issue.

Can implementations optimize this pattern to remove this overhead?

# Andrea Giammarchi (12 years ago)

I think I've completely missed the undefined === absent conversation ... so if I have an object, and obj.key = void 0 why key would be considered absent, exactly? undefined is a very well described "value", passing stuff around thinking has a value but getting ignored by signatures and who knows what else is a door to hell in the real world.

my 2 cents

# K. Gadd (12 years ago)

Dispatching entirely off argument types and not anything else (like count) is an interesting idea. I'm not sure it's on topic for this discussion though. Is there a way to actually do type dispatch in ES at present, let alone efficiently?

Based on my understanding of current VMs, you'd have to do a song and dance (per argument) like this: Is it undefined? Terminate argument scanning. Check typeof to see if it is a special type; dispatch based on that if so. If it's not a special type, check to see if it's null. If so, dispatch based on 'object' since you can't know more about the type than that and have to guess. If it's not null, call Object.getPrototypeOf and then somehow look up that prototype against the type information you're doing overload dispatch against.

It's not clear to me how this would interact with newish features like proxies, either, but I suppose it would at least work. At present doing fast dispatch based on argument count and THEN only doing per-argument type checks if necessary is a huge performance boost because VMs can actually generate relatively fast native code for an arguments.length check. Typeof checks and getPrototypeOf do not generally produce fast code in my experience so it would be problematic to do them for every overloaded call site instead of just call sites that actually care about the types of particular arguments. Doing this type of dispatch is comparatively simple in .NET since for any non-null value you can request a Type object for it (via GetType) and then use that as a key into a lookup table/dictionary - so the operation becomes 'if (x == null) typeId = 0 else typeId = typeTable[x.getType()]' or something along those lines, which you can optimize down to a set of conditionals when the number of types you care about is small. The fact that ES does not allow setting properties on all values means it's impossible to hang type information off values like that.

To put it another way, if I have an overloaded functions with two signatures - like say writeline(s) and writeline(f, s) - then in practice I just dispatch off the argument count. Because there are not multiple signatures with the same count, there's no way the user could have intended to call something else. In use cases where correctness is more important, you'd probably want to rigorously check every argument and dispatch off that.

From a performance perspective I also find the idea of having to 'scan' the arguments list sequentially looking for an undefined sentinel to be pretty gross. Do we really need to introduce the awful null terminated string pattern, along with all its problems, in more places?

# Oliver Hunt (12 years ago)

On Nov 10, 2013, at 2:12 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

I think I've completely missed the undefined === absent conversation ... so if I have an object, and obj.key = void 0 why key would be considered absent, exactly? undefined is a very well described "value", passing stuff around thinking has a value but getting ignored by signatures and who knows what else is a door to hell in the real world.

void <expression>

simply evaluates expression and sets the return continuation value to undefined.

void 0 is identical to |undefined| unless a containing scope has declared a variable named “undefined” locally. void … does not result in a special value.

—Oliver

# Brendan Eich (12 years ago)

Dmitry Soshnikov wrote:

Moreover, for this particular splice example, I don't think the (start, deleteCount, ...rest) is the best signature (not to say, incorrect signature). As again was mentioned, a var-args function seems should just use the ...rest params, and exactly starting from the position when the first optional argument is started. And if it's started right from the position 0 (as with the splice), then probably the more natural signature would be the (...args).

This gives the wrong function.length result, though (as Allen pointed out).

P.S.: I do also agree that it's better to reserve the syntax of (foo: bar) for the optional types (that are used in TypeScript now).

Indeed!

# Brendan Eich (12 years ago)

David Bruant wrote:

Also note that there is likely to be actual computational overhead in both creating a rest argument and in destructuring it. In some cases, that overhead may be an issue. Can implementations optimize this pattern to remove this overhead?

Surely they can. Will they? If it matters.

I'm with you and Mark by default, we have rest, we have defaults, we don't need much more. But the residual arguments.length use-case is vexing.

# David Bruant (12 years ago)

Le 10/11/2013 23:34, Brendan Eich a écrit :

Dmitry Soshnikov wrote:

Moreover, for this particular splice example, I don't think the (start, deleteCount, ...rest) is the best signature (not to say, incorrect signature). As again was mentioned, a var-args function seems should just use the ...rest params, and exactly starting from the position when the first optional argument is started. And if it's started right from the position 0 (as with the splice), then probably the more natural signature would be the (...args).

This gives the wrong function.length result, though (as Allen pointed out).

I wrote in an earlier message that function length is writable, but I was confusing with function name... Sorry about that. Would it make sense to make function length writable?

# Mark Miller (12 years ago)

I hate arguments.length as well. But given that we can't get rid of it, the occasional idiomatic use is not that vexing. In strict mode "arguments" is effectively a reserved word. If the only use of "arguments" within a strict function is "arguments.length", this will be visible both to humans and to tools. Implementations can trivially implement this by without reifying the arguments object, if it matters. "switch (arguments.count) {" simply becomes an idiom that two sorts of people learn:

  1. Those who wish to engage in this anti-pattern.
  2. Those who wish to understand code using the anti-pattern (and possibly berate their authors).
# Andrea Giammarchi (12 years ago)

void 0 is identical to undefined, I perfectly know it, and I have no idea who decided that undefined === absent ..


alert(absent); // this is error


alert(undefined); // this is undefined
var undefined;

I think ignoring undefined, if that's what has been decided, is a mistake. As easy as that.

# Brendan Eich (12 years ago)

Andrea Giammarchi wrote:

I think ignoring undefined, if that's what has been decided, is a mistake. As easy as that.

Read this thread, or past threads:

esdiscuss/2013-September/033406

which links to

esdiscuss/2012-June/023402

where I called for agreement, and

esdiscuss/2012-July/024207

where (find "B. Defaults") the agreement is recorded, with use-case-based rationale.

I sometimes think people don't want to remember what they don't agree with. I find that I do that sometimes -- human nature, not sure if it has a cog-psych name.

Anyway, it's better to remember, or try to re-deduce the rationale, and argue with that. Not just fail to see why and assert to the contrary.

# Mark Miller (12 years ago)

On Sun, Nov 10, 2013 at 2:53 PM, Brendan Eich <brendan at mozilla.com> wrote:

Andrea Giammarchi wrote:

I think ignoring undefined, if that's what has been decided, is a mistake. As easy as that.

Read this thread, or past threads:

esdiscuss/2013-September/033406

which links to

esdiscuss/2012-June/023402

where I called for agreement, and

esdiscuss/2012-July/024207

where (find "B. Defaults") the agreement is recorded, with use-case-based rationale.

I sometimes think people don't want to remember what they don't agree with. I find that I do that sometimes -- human nature, not sure if it has a cog-psych name.

en.wikipedia.org/wiki/Confirmation_bias

Confirmation bias (also called confirmatory bias or myside bias) is a tendency of people to favor information that confirms their beliefs or hypotheses en.wikipedia.org/wiki/Hypothesis.[Note

1]en.wikipedia.org/wiki/Confirmation_bias#cite_note-1 [1] en.wikipedia.org/wiki/Confirmation_bias#cite_note-plous233-2

People display this bias when they gather or remember information selectively, or when they interpret it in a biased wayen.wikipedia.org/wiki/Cognitive_bias .

# Brendan Eich (12 years ago)

Mark Miller wrote:

en.wikipedia.org/wiki/Confirmation_bias

Confirmation bias (also called confirmatory bias or myside bias) is a tendency of people to favor information that confirms their beliefs or hypotheses en.wikipedia.org/wiki/Hypothesis.^[Note 1] en.wikipedia.org/wiki/Confirmation_bias#cite_note-1 ^[1] en.wikipedia.org/wiki/Confirmation_bias#cite_note-plous233-2 People display this bias when they gather or remember information selectively, or when they interpret it in a biased way en.wikipedia.org/wiki/Cognitive_bias.

Quite broad, was hoping for something narrower, with a catchier name ;-).

# Brendan Eich (12 years ago)

David Bruant wrote:

Le 10/11/2013 23:34, Brendan Eich a écrit :

Dmitry Soshnikov wrote:

Moreover, for this particular splice example, I don't think the (start, deleteCount, ...rest) is the best signature (not to say, incorrect signature). As again was mentioned, a var-args function seems should just use the ...rest params, and exactly starting from the position when the first optional argument is started. And if it's started right from the position 0 (as with the splice), then probably the more natural signature would be the (...args).

This gives the wrong function.length result, though (as Allen pointed out). I wrote in an earlier message that function length is writable, but I was confusing with function name... Sorry about that. Would it make sense to make function length writable?

It is configurable non-writable in ES6, by design. Still a pain to farble from its useless default in this case (via Object.defineProperty).

# Andrea Giammarchi (12 years ago)

You replied as if I took part in those conversations and forgot ... that might be the case but AFAIK (indeed) is not.

However, I am not saying that explicit undefined should not result in the default argument value, when specified, I am saying that passing explicitly undefined should not be ignored.

Reading that looks like the agreement is that explicit or not, if an argument has a default, it should be used when undefined is the (un)received value and I agree on that.

Here we are saying that undefined would result in "a non argument" so that whatever(undefined) would have an arguments.length === 0 unless I've misunderstood this part of the thread (which might be as well)

I won't re-quote the part in any case and if I misunderstood then all good and thanks.

# Benjamin (Inglor) Gruenbaum (12 years ago)

From: Allen Wirfs-Brock <allen at wirfs-brock.com>

One of the the few remaining uses of a function's 'arguments' binding is

to determine the actual number of passed arguments. This is necessary in some overloading scenarios where a function has different behavior when an argument is completely absent then it has when undefined (or any other default value) is explicitly passed in that parameter position.

I'd just like to say that I wouldn't rule out a creative solution that'd allow better function overloading. The problem is not the fact the arguments number isn't reachable without the context decided arguments imho.

The problem is that overloading is something that is very useful to do, and we'd like to be able different functions based on the number of parameters. While optional parameters, and rest parameters are really useful and solve a lot of the use cases we'd need overloading for - it's clear that there are use cases it does not address.

I'm not sure that tackling the problem by adding new syntax for the number of params is the best approach (although I'd hate to have to resort to arguments, explicitly or implicitly like in the example given).

# Allen Wirfs-Brock (12 years ago)

On Nov 10, 2013, at 3:31 PM, Benjamin (Inglor) Gruenbaum wrote:

From: Allen Wirfs-Brock <allen at wirfs-brock.com>

One of the the few remaining uses of a function's 'arguments' binding is to determine the actual number of passed arguments. This is necessary in some overloading scenarios where a function has different behavior when an argument is completely absent then it has when undefined (or any other default value) is explicitly passed in that parameter position.

I'd just like to say that I wouldn't rule out a creative solution that'd allow better function overloading. The problem is not the fact the arguments number isn't reachable without the context decided arguments imho.

The problem is that overloading is something that is very useful to do, and we'd like to be able different functions based on the number of parameters. While optional parameters, and rest parameters are really useful and solve a lot of the use cases we'd need overloading for - it's clear that there are use cases it does not address.

I'm not sure that tackling the problem by adding new syntax for the number of params is the best approach (although I'd hate to have to resort to arguments, explicitly or implicitly like in the example given).

Declarative overloading in a dynamically typed language is tricky and programmers with static language experience too often try to pretend that they can do JS overloading just like they would do it in C++ or Java.

I think the only sort of declarative overloading that is appropriate for JS is based upon the number of actually passed arguments (and nothing else). If you agree with that, then there is a pretty straightforward pattern for expressing that in ES6:

function f(...args) { //an overloaded frunction switch (args.length) { case 0: { /* body of the no arguments overload / } case 1: { let <single parameter pattern> ] = args;
/
body of the 1 argument overload / } case 2: { let [ <two parameter pattern> ] = args; / body of the 2 argument overload / } case 3: { let [ <three parameter pattern> ] = args;
/
body of the 3 argument overload / } ... default: { let [ <n + rest parameter pattern> ] = args;
/
body of the n+rest argument overload */ } } }

This is a pattern that would be pretty easy for engines to recognize, avoid creating an actual 'args' object, and map the selected cases destructed parameters directly onto the passed argument on the stack.

# Axel Rauschmayer (12 years ago)

Couldn’t this be handled by pattern matching? Two steps would be necessary to do so (I know that the second one is controversial, but hear me out).

First: introduce a pattern matching construct.

function splice(...args) {
    match(args) {
        case [start, stop, ...item]:
            ...
        case []:
            ...
    }
    ...
}

Second: Matching an array fails if the right-hand side does not have the correct length. Two arguments in favor of this approach: – If you want things to be more lenient, you can always use the rest operator. – To me (subjectively), it feels cognitively right. Matching against [x, y] seems fundamentally different from matching against { 0: x, 1: y }. In the former case, length and order of elements matters, in the latter case, it doesn’t.

The neat things is that #2 would also enable the enforcement of an arity:

function foo(args) { let [must, have, four, params] = args; // exception if arity != 4 }

# Brendan Eich (12 years ago)

Andrea Giammarchi wrote:

You replied as if I took part in those conversations and forgot ... that might be the case but AFAIK (indeed) is not.

You are on the list, this came up less than two months ago. Google site search works, please use it. "site:mail.mozilla.org es-discuss undefined defaulting" is one way, there are others.

However, I am not saying that explicit undefined should not result in the default argument value, when specified, I am saying that passing explicitly undefined should not be ignored.

Reading that looks like the agreement is that explicit or not, if an argument has a default, it should be used when undefined is the (un)received value and I agree on that.

Here we are saying that undefined would result in "a non argument" so that whatever(undefined) would have an arguments.length === 0 unless I've misunderstood this part of the thread (which might be as well)

No, arguments.length would be 1 per ES6 -- that's important for both delegation and varargs-style (arguments.length-based) dispatching.

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

I think the only sort of declarative overloading that is appropriate for JS is based upon the number of actually passed arguments (and nothing else).

Not to pick a fight (yet), but that's a bit strong. Recall I'm proposing value objects with operators dispatching 2-ary functions based on arguments, in a way that is inline-cacheable. Still working on it (Christian Hansen helping), update anon.

# Allen Wirfs-Brock (12 years ago)

On Nov 10, 2013, at 2:43 PM, Mark Miller wrote:

I hate arguments.length as well. But given that we can't get rid of it, the occasional idiomatic use is not that vexing. In strict mode "arguments" is effectively a reserved word. If the only use of "arguments" within a strict function is "arguments.length", this will be visible both to humans and to tools. Implementations can trivially implement this by without reifying the arguments object, if it matters. "switch (arguments.count) {" simply becomes an idiom that two sorts of people learn:

Can someone remind me what we decided about 'arguments' and arrow functions. I pretty sure we discussed this recently and reach some conclusion but I can't find it in either meeting notes or es-discuss. And I don't trust the correctness of what the spec. draft currently says about this.

Any pointers?

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

Can someone remind me what we decided about 'arguments' and arrow functions. I pretty sure we discussed this recently and reach some conclusion but I can't find it in either meeting notes or es-discuss. And I don't trust the correctness of what the spec. draft currently says about this.

Any pointers?

"""

The /Identifier/ primary expression |arguments| may not be used in an arrow function’s body (whether expression or block form).

Likewise, |yield| may not be used in an arrow function’s body. Arrows cannot be generators and we do not want deep continuations.

"""

from harmony:arrow_function_syntax.

Meeting notes back this.

# Domenic Denicola (12 years ago)

As of 6 months ago there was a shift toward treating it the same as super, i.e. use the enclosing scope's. I feel like this was agreed on more recently too. But I am not sure.

esdiscuss.org/topic/introduction-comprehensions-beyond-arrays#content

# Brendan Eich (12 years ago)

Domenic Denicola wrote:

As of 6 months ago there was a shift toward treating it the same as super, i.e. use the enclosing scope's. I feel like this was agreed on more recently too. But I am not sure.

esdiscuss.org/topic/introduction-comprehensions-beyond-arrays#content-1

From esdiscuss.org/topic/introduction-comprehensions-beyond-arrays#content-23 and adjacent (the one after is the last I see), we did not reach agreement, and seemed to go back to arguments in an arrow body expression or block being an error.

We should discuss at this month's TC39 meeting in < 2 weeks. Adding it to the agenda.

# Allen Wirfs-Brock (12 years ago)

On Nov 10, 2013, at 4:53 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

I think the only sort of declarative overloading that is appropriate for JS is based upon the number of actually passed arguments (and nothing else).

Not to pick a fight (yet), but that's a bit strong. Recall I'm proposing value objects with operators dispatching 2-ary functions based on arguments, in a way that is inline-cacheable. Still working on it (Christian Hansen helping), update anon.

Sure, but that is dynamic type based dispatching with caching, not static declarative overloading

We could do static describe overloads of primitive types (and probably value types). Where the usual attempts at describing overloads for JS go wrong is it they either ignore dynamic coercions or forget in in real JS code they would happen after the overload selection. WebIDL address this by building performing (known) coercions into its overload selection algorithm. But I don't think any human JS programer would do it the way it does.

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

Not to pick a fight (yet), but that's a bit strong. Recall I'm proposing value objects with operators dispatching 2-ary functions based on arguments, in a way that is inline-cacheable. Still working on it (Christian Hansen helping), update anon.

Sure, but that is dynamic type based dispatching with caching, not static declarative overloading

That it's dynamic does not mean the form used when defining operator multimethods can't be declarative syntax.

We need to separate static syntax, which I think is better for defining operators (in classes, value class or regular class), and static overload resolution. Agree we can't have the latter.

We could do static describe overloads of primitive types (and probably value types).

And definitely value types -- or to your "dynamic" point, value objects.

Where the usual attempts at describing overloads for JS go wrong is it they either ignore dynamic coercions or forget in in real JS code they would happen after the overload selection. WebIDL address this by building performing (known) coercions into its overload selection algorithm. But I don't think any human JS programer would do it the way it does.

Agreed, although it smells that WebIDL has this kind of overloading at all when JS doesn't. We need to avoid elaborating it, if it is legacy sunk-cost and provided it doesn't infect engines or impose too much hardship on self-hosted overloaded DOM methods, or on JSIDL.

# Boris Zbarsky (12 years ago)

On 11/10/13 5:12 PM, Andrea Giammarchi wrote:

I think I've completely missed the undefined === absent conversation ... so if I have an object, and obj.key = void 0 why key would be considered absent, exactly?

Fwiw, based on feedback from this group this is how WebIDL dictionaries work now: the "key is present" test is basically "obj.key !== undefined" as opposed to "'key' in obj"...

# Boris Zbarsky (12 years ago)

On 11/10/13 8:25 PM, Allen Wirfs-Brock wrote:

Where the usual attempts at describing overloads for JS go wrong is it they either ignore dynamic coercions or forget in in real JS code they would happen after the overload selection. WebIDL address this by building performing (known) coercions into its overload selection algorithm. But I don't think any human JS programer would do it the way it does.

So the way WebIDL works right now is basically a 4-step process:

  1. Select the set of valid overloads for the given argument count (basically, select all the overloads that might be callable with that many arguments, based on which arguments they have optional, etc).

  2. Do dynamic coercions for arguments starting with the first one and up through the last one on which the elements of this set agree.

  3. Select the right overload based on the first argument on which the remaining elements of the overload set disagree.

  4. Finish doing dynamic coercions.

I agree that this is not how anyone would write this in practice. Part of the reason for this is that this algorithm is trying to handle two types of overloading at once: overloading on argument count (step 1) and overloading on type of argument (step 3). And there are complications from dealing with overloads of the "argument type" kind in situations where some of the overloads have (typed!) varargs at the end and whatnot, at least in theory.

In practice, though, APIs generally do one or the other but not both. To do both you'd need an API somewhat like this (in WebIDL):

void foo();
void foo(InterfaceType1 arg);
void foo(InterfaceType2 arg);

where some overloads have to be told apart by number of arguments and some have to be told apart by the types of the arguments.

I just checked for APIs in Gecko's WebIDL where selection is happening based on argument count (in that there are overloads with different argument counts) and there are also some argument counts which have multiple overloads for them. The upshot is that almost all of them can in fact be dealt with by picking an "overload" based on type of the first argument that doesn't match across all the overloads with just a few exceptions:

Document:
   TouchList createTouchList(Touch... touches);
   TouchList createTouchList(sequence<Touch> touches);

IDBFactory:
   IDBOpenDBRequest open(DOMString name, [EnforceRange] unsigned long 
long version);
   IDBOpenDBRequest open(DOMString name, optional IDBOpenDBOptions options);

URL:
   [Constructor(DOMString url, URL base),
    Constructor(DOMString url, optional DOMString base = "about:blank")]
interface URL {

WebSocket:
   [Constructor(DOMString url),
    Constructor(DOMString url, DOMString protocols),
    Constructor(DOMString url, sequence<DOMString> protocols)]

XMLHttpRequest:
   void send();
   void send(ArrayBuffer data);
   void send(ArrayBufferView data);
   void send(Blob data);
   void send(Document data);
   void send(DOMString? data);
   void send(FormData data);
   void send(InputStream data);

I believe even for all of these, we could treat them as overloads on the "type" of some argument, where we allow "undefined" as a type that matches the argument not being present or an argument being optional.

So a viable way forward in terms of WebIDL overloads, perhaps, is to simply disallow overloading on both argument count and argument types (modulo the proviso about undefined above, somehow) and require that overloads for a name either all have an argument that can be used to distinguish them from each other or take different argument counts but not both. Or something.

This does nothing for getting rid of the need to check arguments.length in overloads that overload on the arg count, of course. We could be more radical and recast it all in terms of examining types, treating missing arguments as undefined but that would break compat in at least canvas methods, unfortunately, and I don't think we really want to go there.

On the other hand, what we probably could do if we make the split above is make overloading on arg count explicitly deprecated (e.g. enforce in UAs that no new APIs that use it get added).

# Jonas Sicking (12 years ago)

On Mon, Nov 11, 2013 at 2:12 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

One of the the few remaining uses of a function's 'arguments' binding is to determine the actual number of passed arguments. This is necessary in some overloading scenarios where a function has different behavior when an argument is completely absent then it has when undefined (or any other default value) is explicitly passed in that parameter position. That situation occurs in a number of DOM APIs and even a few ES library functions.

I think the number is very low. I think I've heard of a total of four DOM functions which currently treat "not passed" as different from "explicitly passed undefined". And we're working on changing those so that the two are treated the same. Unclear if we'll be able to change all of them.

So from the DOM point of view, I don't see a lot of reason to add convenience capabilities for detecting that difference.

And in none of the cases in the DOM, does function.length become a problem. This is because in all of the cases the argument truly is optional, which means that .length does not include it. So for example for XMLHttpRequest.open you could write a JS implementation that looks like:

function open(verb, url, ...args) {
  let [async, username, passwd] = args;
  if (args.length == 0) {
    async = true;
  }
  ... more code here ...
}

It is surprising to me that that is not the case in the ECMAScript APIs, but seems like that is history.

# Corey Frang (12 years ago)

Just to provide another way of working around it:

var empty = {}; // or even a symbol?

function splice(start = empty, count = 0, ...items) {
  if (start === empty) { ...
}
# Boris Zbarsky (12 years ago)

On 11/11/13 1:22 AM, Jonas Sicking wrote:

I think the number is very low. I think I've heard of a total of four DOM functions which currently treat "not passed" as different from "explicitly passed undefined". And we're working on changing those so that the two are treated the same. Unclear if we'll be able to change all of them.

This is not true at all.

You're thinking of the few cases that have a "optional boolean foo = true" argument but depend on explicit undefined being treated as false. And even there we know for a fact that we can't change at least one of then (XMLHttpRequest.open).

But there are more APIs that treat "not passed" as different from "undefined" that don't involve default values. CanvasRenderingContext2D.drawImage is a prime example. So, I suspect, is CanvasRenderingContext2D.putImageData. Probably others too; I haven't done an exhaustive search.

So from the DOM point of view, I don't see a lot of reason to add convenience capabilities for detecting that difference.

Well, to the extent that we're not self-hosting the DOM implementation, sure. I mean, for the specific case of SpiderMonkey we get an args.length() no matter what in a JSNative, so obviously we don't have to care too much about whether that's possible in the language itself from that point of view. But that seems a bit shortsighted to me.

And in none of the cases in the DOM, does function.length become a problem.

Well, largely because that's so busted in DOM implementations and specs anyway. ;)

# Mark S. Miller (12 years ago)

On Sun, Nov 10, 2013 at 11:33 PM, Corey Frang <gnarf37 at gmail.com> wrote:

Just to provide another way of working around it:

That is excellent! Has all the upsides of the best of the other proposals and none of the downsides.

# Allen Wirfs-Brock (12 years ago)

No, this doesn't work because then both

[1,2,3].splice()
[1.2.3].splice(undefined)

will both result in start === empty. But for web compatibility they must be treated differently.

# Tab Atkins Jr. (12 years ago)

Right. The one and only way to test for missing args different from undefined is to use a rest arg and test length, as demonstrated earlier in this thread.

# Mark S. Miller (12 years ago)

Doh! My mistake. I was momentarily attributing the old semantics to default parameter assignment, in which case we wouldn't have had the problem in the first place.

# Allen Wirfs-Brock (12 years ago)

On Nov 10, 2013, at 6:12 PM, Boris Zbarsky wrote:

...

I believe even for all of these, we could treat them as overloads on the "type" of some argument, where we allow "undefined" as a type that matches the argument not being present or an argument being optional.

So a viable way forward in terms of WebIDL overloads, perhaps, is to simply disallow overloading on both argument count and argument types (modulo the proviso about undefined above, somehow) and require that overloads for a name either all have an argument that can be used to distinguish them from each other or take different argument counts but not both. Or something.

This does nothing for getting rid of the need to check arguments.length in overloads that overload on the arg count, of course. We could be more radical and recast it all in terms of examining types, treating missing arguments as undefined but that would break compat in at least canvas methods, unfortunately, and I don't think we really want to go there.

On the other hand, what we probably could do if we make the split above is make overloading on arg count explicitly deprecated (e.g. enforce in UAs that no new APIs that use it get added).

My, perhaps more radical, recommendation would be to only do overloading based upon argument counts and to treat all type overloads for a given argument length as union types that are left to a function's implementation to discriminate. That would vastly simplify the WebIDL overload dispatch and pretty much eliminate the idea that WebIDL based methods must be exotic objects with distinctive [[Call]] behavior.

Also, I would outlaw (new) overloads like

Document:
 TouchList createTouchList(Touch... touches);
 TouchList createTouchList(sequence<Touch> touches);

In the general case in JS code it can be quite problematic to distinguish these and hence this pattern really should be used. There is much regret about the main place this pattern occurs in the ES built-ins:

var a = new Array(1,2,3);  //create a three element array with with elements initialized to 1,2,3
var b = new Array(1); //create a sparse array whose length is set to 1;
# Boris Zbarsky (12 years ago)

On 11/11/13 1:37 PM, Allen Wirfs-Brock wrote:

My, perhaps more radical, recommendation would be to only do overloading based upon argument counts and to treat all type overloads for a given argument length as union types that are left to a function's implementation to discriminate.

We could do that, in fact; the problem becomes how to handle the few cases I listed in my mail.

pretty much eliminate the idea that WebIDL based methods must be exotic objects with distinctive [[Call]] behavior.

I don't see how it changes anything in this regard, actually.

Also, I would outlaw (new) overloads like

Document: TouchList createTouchList(Touch... touches); TouchList createTouchList(sequence<Touch> touches);

Sure. This one is fallout from the spec changing from one of those to the other but people not wanting to break compat so supporting both... I don't think anyone likes to create an API like that.

 var a = new Array(1,2,3);  //create a three element array with with elements initialized to 1,2,3
 var b = new Array(1); //create a sparse array whose length is set to 1;

This is actually a different pattern from the createTouchList one above, though I agree it's equally undesirable.

# Corey Frang (12 years ago)

I hate to bring this up, as I'm sure I've missed a bunch of the arguments that define WHY, but if this is the case, it seems unintuitive to me that passing undefined still results in a default param being set.

function test(a = 1) { console.log(a); }

test(); // gets 1 - yay
test(undefined); // gets 1 - boo! Expect: undefined
# Brandon Benvie (12 years ago)

The reason for this is so that you can do:

function foo(bar) {
   return delegatedFoo("foo", bar);
}

function delegatedFoo(foo, bar = "bar") {
   return foo + bar;
}

foo(); // "foobar" and not "fooundefined"

This kind of argument-passing delegation is a very common in JS.

# Corey Frang (12 years ago)

Sure, it makes some sense when you see it like that :)

I retract my "this doesn't make sense"

I guess we are stuck with (...args)

# Brendan Eich (12 years ago)

Allen Wirfs-Brock wrote:

Also, I would outlaw (new) overloads like

Document:
  TouchList createTouchList(Touch... touches);
  TouchList createTouchList(sequence<Touch>  touches);

Yeah, why are new (this is from Web Events, not legacy) APIs doing the crazy. Answer: because people imitate older crazy APIs, and WebIDL supports this.

# Boris Zbarsky (12 years ago)

On 11/12/13 11:24 AM, Brendan Eich wrote:

Yeah, why are new (this is from Web Events, not legacy) APIs doing the crazy. Answer: because people imitate older crazy APIs, and WebIDL supports this.

No. In this case it's because the spec started out one way (with the sequence, iirc) but then got changed to the other way, but implementations had already shipped the first way, so now for compat they support both.

If webidl didn't allow this, they'd just use "any..." and define it all in prose, I assume.

# Brendan Eich (12 years ago)

Boris Zbarsky wrote:

On 11/12/13 11:24 AM, Brendan Eich wrote:

Yeah, why are new (this is from Web Events, not legacy) APIs doing the crazy. Answer: because people imitate older crazy APIs, and WebIDL supports this.

No. In this case it's because the spec started out one way (with the sequence, iirc) but then got changed to the other way, but implementations had already shipped the first way, so now for compat they support both.

Oh, ok -- new-ish legacy. This makes the case for stronger normative specs against such overloading, starting with WebIDL.

If webidl didn't allow this, they'd just use "any..." and define it all in prose, I assume.

Perhaps, and that raises a good point: JS allows all kinds of bad overloading. Doesn't mean we should encourage it in the WebIDL-based specs, though.

# Boris Zbarsky (12 years ago)

On 11/12/13 12:21 PM, Brendan Eich wrote:

Oh, ok -- new-ish legacy. This makes the case for stronger normative specs against such overloading, starting with WebIDL.

Again, WebIDL disallowing this would not have helped; the real issue was a semantic problem.

I'm all for simplifying the overloading that WebIDL supports, but it wouldn't have affected this case.

# Brendan Eich (12 years ago)

Boris Zbarsky wrote:

Oh, ok -- new-ish legacy. This makes the case for stronger normative specs against such overloading, starting with WebIDL.

Again, WebIDL disallowing this would not have helped; the real issue was a semantic problem.

What "semantic problem" required two createTouchList functions having the same name? Isn't that a design choice? If so, then I am arguing it was the wrong choice, but "looked ok" by precedent including WebIDL support.

I'm all for simplifying the overloading that WebIDL supports, but it wouldn't have affected this case.

Whatever I'm trying to affect, the idea is norms, not outright bans or removals of code required for backward compatibility. If createTouchList is relatively new, then I bet there will be a "next time". What spec of doc should promulgate a norm that says "use a different name" for the second createTouchList?

# Boris Zbarsky (12 years ago)

On 11/12/13 7:19 PM, Brendan Eich wrote:

What "semantic problem" required two createTouchList functions having the same name?

The fact that some UAs shipped one version of it others shipped another (as the spec evolved), then the working group decided to spec one of those versions but there was already code in the wild using the other version, so those UAs are now just supporting both signatures to avoid breaking that code.

Isn't that a design choice?

Sure. A bad one; the working group should have changed the name when they changed the semantics.

If so, then I am arguing it was the wrong choice, but "looked ok" by precedent including WebIDL support.

It didn't look ok. They just made a backwards-incompatible change, period.

Whatever I'm trying to affect, the idea is norms

Sure. My point is that in this particular case the "norm" of overloads in WebIDL wasn't the real norm that got broken. The "don't break shipping stuff" norm was the real issue.

What spec of doc should promulgate a norm that says "use a different name" for the second createTouchList?

You mean "don't effing randomly break backwards compat"? No spec for that, sadly.

# hacksmw (12 years ago)

How Function.getArguments()?

Function.getArguments() should return the arguments array.

let args = Function.getArguments(); if(args.length != 2) throw new Error("invalid arguments");

-- View this message in context: mozilla.6506.n7.nabble.com/an-idea-for-replacing-arguments-length-tp298172p299750.html Sent from the Mozilla - ECMAScript 4 discussion mailing list archive at Nabble.com.