Function length

# Irakli Gozalishvili (13 years ago)

I just noticed strange behavior in spider monkey implementation of rest arguments:

(function(a, b, ...rest) {}).length // => 2

I think ignoring rest in length is pretty counter intuitive. For example I have small utility function that I use to dispatch depending on argument length.

var sum = dispatcher([ function() { return 0 }, function(x) { return x }, function(x, y) { return x + y }, function(x, y, z) { return Array.prototype.slice.call(arguments, 1).reduce(sum, x) } ])

This behavior of rest would obviously break assumptions made by this library. That being said I don't think 3 would be any better. Maybe such functions length should be:

  • Infinity ?
  • 2.5 ?

That way libraries would be able to handle them in a nice way:

var sum = dispatcher([ () => 0, (x) => x, (x, y) => x + y, (x, ...rest) => rest.reduce(sum, x) ])

-- Irakli Gozalishvili Web: www.jeditoolkit.com

# Erik Arvidsson (13 years ago)

Don't you want to use arguments.length instead of function.length?

# Rick Waldron (13 years ago)

On Saturday, June 9, 2012 at 11:53 PM, Erik Arvidsson wrote:

Don't you want to use arguments.length instead of function.length? On Jun 9, 2012 6:53 PM, "Irakli Gozalishvili" <rfobic at gmail.com (mailto:rfobic at gmail.com)> wrote:

I just noticed strange behavior in spider monkey implementation of rest arguments:

(function(a, b, ...rest) {}).length // => 2

I agree that this is strange, I would expect 3, as there are three formally named parameters.

# Erik Arvidsson (13 years ago)

On Sat, Jun 9, 2012 at 9:08 PM, Rick Waldron <waldron.rick at gmail.com> wrote:

I just noticed strange behavior in spider monkey implementation of rest arguments:

(function(a, b, ...rest) {}).length // => 2

I agree that this is strange, I would expect 3, as there are three formally named parameters.

The length in ES5 is not very consistent.

For ES6 we decide that we wanted the function length to be used to tell how many required parameters a function has.

Today rest params are done using arguments:

(function(a, b) { var rest = Array.prototype.slice.call(arguments, 2); }).length // => 2

The rule is that optional and rest parameters are not included in the length of the function.

# Allen Wirfs-Brock (13 years ago)

On Jun 9, 2012, at 6:52 PM, Irakli Gozalishvili wrote:

I just noticed strange behavior in spider monkey implementation of rest arguments:

(function(a, b, ...rest) {}).length // => 2

That answer is consistent with what is specified in the ES6 draft spec. The actual value is specified algorithmically and is summarized by this note:

NOTE The ExpectedArgumentCount of a FormalParameterList is the number of FormalParameters to the left of either the rest parameter or the first FormalParameter with an Initialiser. A FormalParameter without an initializer are allowed after the first parameter with an initializer but such parameters are considered to be optional with undefined as their default value.

See section 13.1.

The draft is based upon the conclusions that were reached when this was last discussed. See the thread starting esdiscuss/2011-August/016361

There is no obviously "right" answer to what should be reported as the length (and it isn't clear whether this property really has any utility). The closest thing we have to legacy precedent are these statement from previous versions of the spec:

15 Every built-in Function object described in this clause—whether as a constructor, an ordinary function, or both—has a length property whose value is an integer. Unless otherwise specified, this value is equal to the largest number of named arguments shown in the subclause headings for the function description, including optional parameters.

15.3.5.1 The value of the length property is an integer that indicates the “typical” number of arguments expected by the function.

Note that that the the legacy description is not particularly self consistent and that where a length value is "other specified" for various built-in functions it tends to follow the 15.3.5.1 rule.

# Irakli Gozalishvili (13 years ago)

Never the less problem still stands, but maybe there are other ways to solve it. Only solution I'm left with so far is utility function like this:

function hasRest(f) { return !!~String(f).split('\n').shift().indexOf('...') }

Which is bad, specially toString of function is not guaranteed to return source.

Maybe alternative solution could be some standard function / method to test if the function captures ...rest args.

-- Irakli Gozalishvili Web: www.jeditoolkit.com

# John Tamplin (13 years ago)

On Sun, Jun 10, 2012 at 12:48 PM, Irakli Gozalishvili <rfobic at gmail.com>wrote:

Never the less problem still stands, but maybe there are other ways to solve it. Only solution I'm left with so far is utility function like this:

function hasRest(f) { return !!~String(f).split('\n').shift().indexOf('...') }

Which is bad, specially toString of function is not guaranteed to return source.

Maybe alternative solution could be some standard function / method to test if the function captures ...rest args.

You could imagine a method like Function.prototype.hasRestArgs, but that wouldn't catch the existing varargs-style coding based on arguments.

# Andreas Rossberg (13 years ago)

On 10 June 2012 03:52, Irakli Gozalishvili <rfobic at gmail.com> wrote:

I just noticed strange behavior in spider monkey implementation of rest arguments:

(function(a, b, ...rest) {}).length // => 2

I think ignoring rest in length is pretty counter intuitive. For example I have small utility function that I use to dispatch depending on argument length.

var sum = dispatcher([   function() { return 0 },   function(x) { return x },   function(x, y) { return x + y },   function(x, y, z) { return Array.prototype.slice.call(arguments, 1).reduce(sum, x) } ])

I don't think any library should ever rely on f.length. It is not a reliable source of information (f might use 'arguments' even when the length is officially 0), and I don't honestly see it being useful for anything but tertiary debugging purposes.

# Irakli Gozalishvili (13 years ago)

I don't think any library should ever rely on f.length.

That's a wrong attitude, there always will be legitimate uses of any feature, otherwise such features are just harmful & IMO should be deprecated / removed.

It is not a reliable source of information (f might use 'arguments' even when the length is officially 0), and I don't honestly see it being useful for anything but tertiary debugging purposes.

In some cases weather function captures rest arguments via arguments is irrelevant. Like in a case I've pointed out earlier. Library provides arity based dispatch based on f.length, so if you pass function() { arguments…. } it will never be called with more than 0 arguments.

-- Irakli Gozalishvili Web: www.jeditoolkit.com

# Allen Wirfs-Brock (13 years ago)

On Jun 11, 2012, at 10:56 AM, Irakli Gozalishvili wrote:

I don't think any library should ever rely on f.length.

That's a wrong attitude, there always will be legitimate uses of any feature, otherwise such features are just harmful & IMO should be deprecated / removed.

Let me try again. We don't understand your use case. You didn't show us the definition of your dispatch function so we have to guess. Even so, It is hard to imagine a "legitimate" use with dynamically provided functions, particularly as the length values assigned to the existing built-ins don't follow strict rules. At the very least you need to help us understand why your use case is both reasonable and valid.

# Brendan Eich (13 years ago)

I would not mind removing Function 'length' but on the web you cannot deprecate and any browser daring to remove will appear broken to users not involved in the content or the engine, and users switch browsers.

Anyway, back to reality: foo.length is in ECMA-262 and we need to spec how it works in the presence of a trailing rest parameter. Allen has drafted something based on discussion here. It's a plausible design and hard to criticize without both your use-case (in detail) and a better alternative.

# Irakli Gozalishvili (13 years ago)

Sorry for not being clear about this. Here is a simplified example of the implementation: gist.github.com/2911817

Also this is just a single particular example, but I expect there to be more. I think what I'm really asking for is a way to know if …rest is being used.

Also IMO arrow functions should not have arguments at all.

-- Irakli Gozalishvili Web: www.jeditoolkit.com

# Mariusz Nowak (13 years ago)

I find Function 'length' as very useful property (I use it in some low-level functional stuff). I also think that defining functions so it reflects only required arguments is very sane decision. In that light I would also expect ...rest to not be counted in Function length.

+1 for keeping it, the way it is.

Brendan Eich-2 wrote:

I would not mind removing Function 'length' but on the web you cannot deprecate and any browser daring to remove will appear broken to users not involved in the content or the engine, and users switch browsers.

Anyway, back to reality: foo.length is in ECMA-262 and we need to spec how it works in the presence of a trailing rest parameter. Allen has drafted something based on discussion here. It's a plausible design and hard to criticize without both your use-case (in detail) and a better alternative.

/be

Irakli Gozalishvili wrote:

I don't think any library should ever rely on f.length.

That's a wrong attitude, there always will be legitimate uses of any feature, otherwise such features are just harmful & IMO should be deprecated / removed.

It is not a reliable source of information (f might use 'arguments' even when the length is officially 0), and I don't honestly see it being useful for anything but tertiary debugging purposes.

In some cases weather function captures rest arguments via arguments is irrelevant. Like in a case I've pointed out earlier. Library provides arity based dispatch based on f.length, so if you pass function() { arguments…. } it will never be called with more than 0 arguments.

-- Irakli Gozalishvili Web: www.jeditoolkit.com

On Monday, 2012-06-11 at 05:33 , Andreas Rossberg wrote:

On 10 June 2012 03:52, Irakli Gozalishvili <rfobic at gmail.com <mailto:rfobic at gmail.com>> wrote:

I just noticed strange behavior in spider monkey implementation of rest arguments:

(function(a, b, ...rest) {}).length // => 2

I think ignoring rest in length is pretty counter intuitive. For example I have small utility function that I use to dispatch depending on argument length.

var sum = dispatcher([ function() { return 0 }, function(x) { return x }, function(x, y) { return x + y }, function(x, y, z) { return Array.prototype.slice.call(arguments, 1).reduce(sum, x) } ])

I don't think any library should ever rely on f.length. It is not a reliable source of information (f might use 'arguments' even when the length is officially 0), and I don't honestly see it being useful for anything but tertiary debugging purposes.

/Andreas


es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss


es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss


Mariusz Nowak

medikoo

# Brendan Eich (13 years ago)

Irakli Gozalishvili wrote:

Sorry for not being clear about this. Here is a simplified example of the implementation: gist.github.com/2911817

Also this is just a single particular example, but I expect there to be more. I think what I'm really asking for is a way to know if …rest is being used.

Your code doesn't work on a function that uses arguments the old fashioned way, though.

Yes, you can make ...rest affect .length and make a dispatcher that counts on that, but it's a just-so story and a hard case. Hard cases make bad law. If it really matters, we can provide better reflection facilities, but I'm pretty sure it doesn't occur enough to justify doing so now.

So use the toString hack if you must and let's see if this use-case becomes hot.

Also IMO arrow functions should not have arguments at all.

That's already in the proposal:

harmony:arrow_function_syntax

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

# Felix Böhm (13 years ago)

The big question is whether Function#length should represent minimum or the maximum amount of arguments. That means that a rest parameter could have zero elements, and optional parameters could fall back to their default value. The minimum approach wouldn't count them, the maximum would expect the most and return positive infinity as soon as the rest parameter is available.

Both approaches have their right to exist. With the maximum approach, the length property could even become a setter: By setting it, the values always default to the defined value, and rest parameters have a maximum length.

It would be nice to get both getters.

(Brendan, I'm sorry for sending you this message twice.)

2012/6/11 Brendan Eich <brendan at mozilla.org>

# Russell Leggett (13 years ago)

On Mon, Jun 11, 2012 at 3:50 PM, Brendan Eich <brendan at mozilla.org> wrote:

Irakli Gozalishvili wrote:

Sorry for not being clear about this. Here is a simplified example of the implementation: gist.github.com/**2911817, gist.github.com/2911817

Also this is just a single particular example, but I expect there to be more. I think what I'm really asking for is a way to know if …rest is being used.

Your code doesn't work on a function that uses arguments the old fashioned way, though.

Yes, you can make ...rest affect .length and make a dispatcher that counts on that, but it's a just-so story and a hard case. Hard cases make bad law. If it really matters, we can provide better reflection facilities, but I'm pretty sure it doesn't occur enough to justify doing so now.

So use the toString hack if you must and let's see if this use-case becomes hot.

I think this is a pretty creative snippet of code, but ultimately unreliable for the reasons mentioned. It does bring up something else, though, that I've avoided mentioning so far, which is pattern matching. I haven't mentioned it because there is clearly a strawmanstrawman:pattern_matchingfor

it, and that never made it to harmony, but in light of this thread I wanted to reiterate how useful it would be. It would not really help function.length, but would address the real underlying problem that the arity dispatcher is trying to tackle. In a language without overloading, the ability to do pattern matching would be an excellent solution to a very common problem. We already have destructuring, it seems like such a small jump to pattern matching. Hard to tell from looking at the strawman why that never made it. If its a matter or feature bloat, I would rate that higher than some other things like default args or array comprehensions.

# Brendan Eich (13 years ago)

Russell Leggett wrote:

It does bring up something else, though, that I've avoided mentioning so far, which is pattern matching. I haven't mentioned it because there is clearly a strawman strawman:pattern_matching for it, and that never made it to harmony, but in light of this thread I wanted to reiterate how useful it would be. It would not really help function.length, but would address the real underlying problem that the arity dispatcher is trying to tackle. In a language without overloading, the ability to do pattern matching would be an excellent solution to a very common problem. We already have destructuring, it seems like such a small jump to pattern matching.

You are preacher, dherman and I are choir. Maybe patterns will make ES7. We shall try again.

Hard to tell from looking at the strawman why that never made it. If its a matter or feature bloat, I would rate that higher than some other things like default args or array comprehensions.

See March 2011 TC39 meeting notes, second day.

Quotes below. Note some fine specimens of TC39's future-proofing fetishization on parade. I will say no more, as I think Dave agrees patterns were not fully baked.

The way to get refutable matching into ES7 is to work now to address all the valid worries, and say why the other worries are false.

/be

[From esdiscuss/2011-March/013403]

Refutable matching and switch extensions: Multiple objections to syntax chosen for pattern-matching switch: colon vs. no colon after default clause, need for blocks, etc. Refutable matching doesn't retrofit into imperative switch syntax well. Waldemar: Refutable matching is half-baked at this point, with too many syntactic and semantic problems. Not clear it's worth its added complexity.

The refutable matching wiki has the following consequences on irrefutable matching: Pattern [x,y]: Matched to [3,4], produces x=3, y=4. Matched to [3,4,5], produces x=3, y=4. Matched to [3], produces x=undefined, y=undefined. (wiki spec bug.) Pattern [..., x, y]: Matched to [3,4], produces x=3, y=4. Matched to [3], looks up negative array indices. (wiki spec bug.)

Pattern [x,y] behaves like [x,y,...] for refutable matching. (wiki spec bug.) Can't match on zero-length arrays. (wiki spec bug?)

Lucas: Feature value should overcome complexity costs. Waldemar: if guards introduce unknown syntactic and semantic complexity Waldemar: where can you use refutable matching outside of switch/match statements and perhaps catch guards? switch/match statements are too heavyweight and differ too much from irrefutable matching assignment; catching doesn't really need destructuring but benefits from conditions. The typical usage (as in Perl) is to use them in if statements: if (pattern =~ expr) {we have matched!}

catch({z,w} if z< w): OK, but then you can't get to the entire exception object from the catch clause. catch(z if z instanceof T): Useful

Waldemar: Refutable matching should integrate trademarking to be compelling. Concern about backing ourselves into a corner by implementing irrefutable pattern matching in catch guards that will later preclude refutable matching. Brendan's example: catch({x, y}) would succeed on {x:3} now but fail later if we change to refutable pattern matching.

# Russell Leggett (13 years ago)

On Tue, Jun 12, 2012 at 1:06 PM, Brendan Eich <brendan at mozilla.org> wrote:

Russell Leggett wrote:

It does bring up something else, though, that I've avoided mentioning so far, which is pattern matching. I haven't mentioned it because there is clearly a strawman <** doku.php?id=strawman:pattern_**matchingstrawman:pattern_matching> for it, and that never made it to harmony, but in light of this thread I wanted to reiterate how useful it would be. It would not really help function.length, but would address the real underlying problem that the arity dispatcher is trying to tackle. In a language without overloading, the ability to do pattern matching would be an excellent solution to a very common problem. We already have destructuring, it seems like such a small jump to pattern matching.

You are preacher, dherman and I are choir. Maybe patterns will make ES7. We shall try again.

Hard to tell from looking at the strawman why that never made it. If its

a matter or feature bloat, I would rate that higher than some other things like default args or array comprehensions.

See March 2011 TC39 meeting notes, second day.

Quotes below. Note some fine specimens of TC39's future-proofing fetishization on parade. I will say no more, as I think Dave agrees patterns were not fully baked.

The way to get refutable matching into ES7 is to work now to address all the valid worries, and say why the other worries are false.

This thread gave me an interesting idea on how to possibly attack pattern matching in ES6 with no new syntax, and still leave room for more sugar later. It actually comes from thinking about the original issue with function.length and using it for arity-based dispatch. What if we just gave a better method than length? What if we had something like function.matches(args)? Where it would return true if all arguments were bound, and no parameters resulted in an undefined binding. function add(a,b){ return a + b; }

add.matches(1,2); // => true
add.matches(1); // => false
add.matches(1,2,3);  => false

This still suffers from the same problem as function.length, and when dealing with simple arity and no destructuring/rest params, would act exactly the same. However, Irakli's dispatcher utility does lay the groundwork for something more interesting.

function makePoint(x,y){ return {x:x,y:y}; }
function drawLine({x:x1,y:y1},{x:x2,y:y2}){...}

let p1 = makePoint(1,2), p2 = makePoint(3,4);
drawLine.matches(p1,p2); // => true
drawLine.matches(1,2); // => false
drawLine.matches({x:1}, {y:2}) // => false
//only has to be a structural subset
drawLine.matches({x:1,y:2,z:3}, {x:1, y:2}) // => true

With that simple boolean function, Irakli's dispatcher utility could be rewritten to loop through the list and check for a match instead of by arity. It would work correctly for rest parameters, but even more interesting, would work with all of the destructuring patterns.

let drawLine = dispatcher(
    (x1,y1,x2,y2) => ...,
    ({x:x1,y:y1},{x:x2,y:y2}) => ...
);

Or a more functional replacement for switch

let result = match(value,
    ({x,y,z}) => "3d"
    ({x,y}) => "2d"
    (...anything) => "not a point"
);

Its not as nice as full pattern matching with literals and wildcards, but it could be pretty clean. If guards were added later, that would be an obvious fit. If it became a popular pattern, we could pave the cowpath with some sugar, and add the things that are missing now.

One of the benefits of getting started this way is that it could be shimmed to just use length.

# Tom Ellis (13 years ago)

I like the look of this:

function add(a,b){
    return a + b;
}

add.matches(1,2); // => true
add.matches(1); // => false
add.matches(1,2,3);  => false

I've never had the need to use function.length, I probably will at some point though.

If you did something like this, using the rest argument:

function add( a, b, ...others ) { ... }

Then the following should occur:

add.matches(1,2) // => true

add.matches(1) //=> false

add.matches(1,3,4) //=> true

Tom

# Andreas Rossberg (13 years ago)

On 12 June 2012 23:57, Russell Leggett <russell.leggett at gmail.com> wrote:

This thread gave me an interesting idea on how to possibly attack pattern matching in ES6 with no new syntax, and still leave room for more sugar later. It actually comes from thinking about the original issue with function.length and using it for arity-based dispatch. What if we just gave a better method than length? What if we had something like function.matches(args)? Where it would return true if all arguments were bound, and no parameters resulted in an undefined binding.     function add(a,b){         return a + b;     }

add.matches(1,2); // => true     add.matches(1); // => false     add.matches(1,2,3);  => false

OK, I'll bite. I see lots of problems and unnecessary complexity here.

If I understand this correctly, then it will require every function closure to include meta information for performing the associated pattern match. Or, when you actually want this to be optimised, the ability to generate (probably lazily) and attach pattern matching code to every function closure. That seems pretty intrusive.

Also, the programmer would have to create a closure for every match arm you ever want to use (unless the compiler is "sufficiently clever" to recognise rather non-trivial patterns, which I doubt). That is likely to be pretty costly. I'm also not sure how you envision actually binding variables during a match. Are you suggesting that one does something like:

if (f.matches(x, y)) f(x, y)

Even if this is abstracted into some 'match' function, a match would redundantly decompose x, y for f twice.

And just to be clear, this feature also reveals implementation details of a function that it shouldn't. For example, it allows you to observe cases where a function ignores some of its documented arguments, or some parts of its arguments. So it has all the bad properties of the 'isBound' function we discussed earlier.

I'd say (despite being part of the pattern matching choir) that this is not the proper way to introduce a feature that, as a proper language construct, would be relatively simple, efficient, and independent of anything else.

# Russell Leggett (13 years ago)

On Wed, Jun 13, 2012 at 5:29 AM, Andreas Rossberg <rossberg at google.com>wrote:

On 12 June 2012 23:57, Russell Leggett <russell.leggett at gmail.com> wrote:

This thread gave me an interesting idea on how to possibly attack pattern matching in ES6 with no new syntax, and still leave room for more sugar later. It actually comes from thinking about the original issue with function.length and using it for arity-based dispatch. What if we just gave a better method than length? What if we had something like function.matches(args)? Where it would return true if all arguments were bound, and no parameters resulted in an undefined binding. function add(a,b){ return a + b; }

add.matches(1,2); // => true
add.matches(1); // => false
add.matches(1,2,3);  => false

OK, I'll bite. I see lots of problems and unnecessary complexity here.

If I understand this correctly, then it will require every function closure to include meta information for performing the associated pattern match. Or, when you actually want this to be optimised, the ability to generate (probably lazily) and attach pattern matching code to every function closure. That seems pretty intrusive.

I'll respond a little out of order by saying that you believe patterns independently implemented would be simple and efficient. If that is the case (and I would think it was), then I don't really see how your argument holds water. Let's just say we actually had patterns. I'll make an imaginary syntax for them.

let pattern = #({x,y});
pattern.matches({x:1,y:2});

If my example pattern could be done simply and efficiently, then why couldn't the same be done for function arguments. The only meta-data you would need to store is exactly the pattern that is in the function parameters. The body of the function provides no additional data. You could either eagerly generate the pattern, or simply keep the parameter pattern ast in the most efficient form to generate it lazily.

The simple (and overwhelmingly most common case) would just be a list of parameters with no rest and no destructuring. Matching against this would reduce to a simple arity check.

Also, the programmer would have to create a closure for every match arm you ever want to use (unless the compiler is "sufficiently clever" to recognise rather non-trivial patterns, which I doubt). That is likely to be pretty costly.

I guess to me this is just functional programming - its effectively a callback for each pattern. The one which matches gets called. I don't see why its so different than using forEach instead of a for loop. One is more expressive and functional style, the other is more efficient.

I'm also not sure how you envision actually binding variables during a match. Are you suggesting that one does something like:

if (f.matches(x, y)) f(x, y)

Even if this is abstracted into some 'match' function, a match would redundantly decompose x, y for f twice.

That is true. Perhaps a builtin Object.match function could optimize? Still, it does not seem overwhelmingly inefficient, just not as nice as built in pattern matching.

And just to be clear, this feature also reveals implementation details of a function that it shouldn't. For example, it allows you to observe cases where a function ignores some of its documented arguments, or some parts of its arguments. So it has all the bad properties of the 'isBound' function we discussed earlier.

I mostly disagree. The signature of a function is it's interface. Even Java let's you reflectively ask about a method's signature. In reality, it is almost no different than function.length in terms of revealing implementation details. My major problem with isBound is that it required looking at the body of the function, because |this| isn't something you'll see in a parameter list.

However, as I said, I only mostly disagree. The same reason I initially cringed at Irakli's function.length request because I worried how it would be abused, I have some of the same worries here. It could make for some pretty subtle code to change what arguments you send a callback based on its signature – but unlike function.isBound which I think had fewer appropriate use cases and looked at the method body, I think function.matches has a much clearer use case and does not expose too much. I especially like it in the case of using it with arrow functions, which have no |this| and no access to an arguments object. I debated about suggesting it only be available on arrow functions, but I think there are some big downsides to that too.

I'd say (despite being part of the pattern matching choir) that this is not the proper way to introduce a feature that, as a proper language construct, would be relatively simple, efficient, and independent of anything else.

I would much prefer to have it as a full, proper language construct, and I would not have suggested function.matches if I thought it would be future unfriendly to adding them. On the contrary, my hope is that this would be a low footprint feature, but would provide the tool missing to allow for the community to play with patterns. One of the great things about JavaScript is its flexibility, and the community has really used that to create the cowpaths we later pave. As a way of figuring out the best way to add full patterns to the language, wouldn't it be helpful to give people this small feature and see what the result is? The more pattern-like destructuring becomes, the more people will want to use it for that purpose. This is the hook needed to easily expose that. And even if pattern-matching becomes first class, I think there will still be cases when functions-as-patterns would still be useful.

# Andreas Rossberg (13 years ago)

On 13 June 2012 15:25, Russell Leggett <russell.leggett at gmail.com> wrote:

On Wed, Jun 13, 2012 at 5:29 AM, Andreas Rossberg <rossberg at google.com>

If I understand this correctly, then it will require every function closure to include meta information for performing the associated pattern match. Or, when you actually want this to be optimised, the ability to generate (probably lazily) and attach pattern matching code to every function closure. That seems pretty intrusive.

I'll respond a little out of order by saying that you believe patterns independently implemented would be simple and efficient. If that is the case (and I would think it was), then I don't really see how your argument holds water. Let's just say we actually had patterns. I'll make an imaginary syntax for them.

let pattern = #({x,y});     pattern.matches({x:1,y:2});

Well, that's not how other languages do pattern matching, or what the strawman proposes. Rather, you usually have a generalisation of what's the switch statement in JS, see the strawman. It makes matching, binding and dispatching into a single operation that can be compiled into efficient decision trees fairly easily (although JS's getters make matters less pleasant).

Also, the programmer would have to create a closure for every match arm you ever want to use (unless the compiler is "sufficiently clever" to recognise rather non-trivial patterns, which I doubt). That is likely to be pretty costly.

I guess to me this is just functional programming - its effectively a callback for each pattern. The one which matches gets called. I don't see why its so different than using forEach instead of a for loop. One is more expressive and functional style, the other is more efficient.

The different with forEach is that there is only one closure, and it's usually executed. In the case of a match, you'll have to create N closures while knowing that at most 1 will actually be called.

I'd say (despite being part of the pattern matching choir) that this is not the proper way to introduce a feature that, as a proper language construct, would be relatively simple, efficient, and independent of anything else.

I would much prefer to have it as a full, proper language construct, and I would not have suggested function.matches if I thought it would be future unfriendly to adding them. On the contrary, my hope is that this would be a low footprint feature, but would provide the tool missing to allow for the community to play with patterns. One of the great things about JavaScript is its flexibility, and the community has really used that to create the cowpaths we later pave. As a way of figuring out the best way to add full patterns to the language, wouldn't it be helpful to give people this small feature and see what the result is?

That's the thing: the feature you suggest is only superficially "small". Once you look closer it actually is much bigger and more intrusive than what's in the strawman. You avoid new syntax, but that's a small win compared to the (significantly!) larger complexity in terms of semantic interference and efficient implementation.

It's good to play with ideas, but if your primary motivation really is getting something accepted into the standard, then I'm not sure that this one would be an easier sell.

# Russell Leggett (13 years ago)

On Wed, Jun 13, 2012 at 12:42 PM, Andreas Rossberg <rossberg at google.com>wrote:

On 13 June 2012 15:25, Russell Leggett <russell.leggett at gmail.com> wrote:

On Wed, Jun 13, 2012 at 5:29 AM, Andreas Rossberg <rossberg at google.com>

If I understand this correctly, then it will require every function closure to include meta information for performing the associated pattern match. Or, when you actually want this to be optimised, the ability to generate (probably lazily) and attach pattern matching code to every function closure. That seems pretty intrusive.

I'll respond a little out of order by saying that you believe patterns independently implemented would be simple and efficient. If that is the case (and I would think it was), then I don't really see how your argument holds water. Let's just say we actually had patterns. I'll make an imaginary syntax for them.

let pattern = #({x,y});
pattern.matches({x:1,y:2});

Well, that's not how other languages do pattern matching, or what the strawman proposes. Rather, you usually have a generalisation of what's the switch statement in JS, see the strawman. It makes matching, binding and dispatching into a single operation that can be compiled into efficient decision trees fairly easily (although JS's getters make matters less pleasant).

Right, I understand that. Yeah, I suppose my syntax was a little odd towards helping my case. However, there is also this bit from Waldemar:

"Waldemar: where can you use refutable matching outside of switch/match statements and perhaps catch guards? switch/match statements are too heavyweight and differ too much from irrefutable matching assignment; catching doesn't really need destructuring but benefits from conditions. The typical usage (as in Perl) is to use them in if statements: if (pattern =~ expr) {we have matched!}"

If I could have my dream version of matching, it would probably be similar to scala/haskell/erlang. We could maybe use "case" instead of "switch" at the top. Not sure if that's confusing, but haskell/erlang use it and its already reserved. Something like:

case p of {
    [x,y] => ...
    {x,y} => ...
    *     => throw ...
}

Which I would greatly prefer over the modified switch statement version. It would be an expression, and have an intuitive tie in to arrow functions. As you can see from this syntax, though, it's very similar to what can be achieved using a match function taking arrow function arguments. But even this form could be reduced down to a single pattern returning true, and a default returning false and that would be the equivalent of a matches method.

Also, the programmer would have to create a closure for every match arm you ever want to use (unless the compiler is "sufficiently clever" to recognise rather non-trivial patterns, which I doubt). That is likely to be pretty costly.

I guess to me this is just functional programming - its effectively a callback for each pattern. The one which matches gets called. I don't see why its so different than using forEach instead of a for loop. One is more expressive and functional style, the other is more efficient.

The different with forEach is that there is only one closure, and it's usually executed. In the case of a match, you'll have to create N closures while knowing that at most 1 will actually be called.

Depending on the way you used it, the set of closures could easily be reused for multiple calls, the same way that Irakli's dispatcher helper works. My point was more that developers have to weigh the tradeoffs for expressivity vs efficiency now, and this would be no different.

I'd say (despite being part of the pattern matching choir) that this is not the proper way to introduce a feature that, as a proper language construct, would be relatively simple, efficient, and independent of anything else.

I would much prefer to have it as a full, proper language construct, and I would not have suggested function.matches if I thought it would be future unfriendly to adding them. On the contrary, my hope is that this would be a low footprint feature, but would provide the tool missing to allow for the community to play with patterns. One of the great things about JavaScript is its flexibility, and the community has really used that to create the cowpaths we later pave. As a way of figuring out the best way to add full patterns to the language, wouldn't it be helpful to give people this small feature and see what the result is?

That's the thing: the feature you suggest is only superficially "small". Once you look closer it actually is much bigger and more intrusive than what's in the strawman. You avoid new syntax, but that's a small win compared to the (significantly!) larger complexity in terms of semantic interference and efficient implementation.

I'll just have to defer to you on that. If its really large, I guess I'll have to accept that, it just doesn't seem as large as you say it is. For any function with vanilla es5 parameters, its simply an arity check. Is the length the same as the number of arguments? That number is already known and available as function.length, and should add no extra overhead to functions with no special parameter syntax. Beyond that, the work to do the match does not seem like it should be much beyond what already has to happen for destructuring. For destructuring, you still have to handle the mismatch case, and bind to undefined. Performing a refutable match would just fail in those cases. I must be oversimplifying.

It's good to play with ideas, but if your primary motivation really is getting something accepted into the standard, then I'm not sure that this one would be an easier sell.

I'm not trying to get something into the standard for the sake of getting it in if that's what you mean. It genuinely seemed useful and still does. I can think of a dozen variations on this which would be useful. And one other thing it does well that pattern matching using a switch does not, is actually look like pattern based dispatch in the cases where you want to match on multiple args, and not just a single value. It also allows for a sort of reified pattern/rule than can be played with by libraries/apis. In the same way that the right low level tools have allowed for people to write libraries for classes/traits/promises etc. this could be a building block to allow for new types of expressive apis.

# David Herman (13 years ago)

Here are my thoughts.

  • Despite the fact that arguments and lack of arity-checking makes it unreliable, JS exposes .length and it's supposed to mean the number of "normal" or "expected" arguments. This suggests it should not include defaulted arguments, and should not include rest-arguments.

  • ES6 makes it unnecessary to use arguments since defaults and rest-args can do everything it can do but in more convenient ways.

  • This suggests that .length could become a more reliable source of information if people move away from using arguments.

  • And yet it's still not the whole story. If you want to introspect a function's arity, you'd really like to know the full range of acceptable arity information. For example, Racket actually provides quite a bit of information about a procedure' arity:

    bit.ly/McZxQt

  • Racket's API is super complex. But I could see at least offering a Function.prototype.hasRestArg getter.

Dave

Given that we already expose .length and (despite the fact that I agree it's unreliable) people do seem to use it from time to time,