Add call and apply methods to RegExp.prototype
Sounds like a good idea to me. On the other hand, this can already easily be done in ES3.
Yes, its easy to pull off oneself, so I don't care much other way. Still, it
seems pretty weird to me to be able to do regex(str)
but not
regex.call(context, str)
. This is accentuated when typeof returns
"function" for regexes (though it seems ES4 will change this to "object").
On Dec 21, 2007, at 7:33 AM, StevenLevithan wrote:
Yes, its easy to pull off oneself, so I don't care much other way.
Still, it seems pretty weird to me to be able to doregex(str)
but notregex.call(context, str)
. This is accentuated when typeof returns "function" for regexes (though it seems ES4 will change this to
"object").
Hey Steve, Yuh-Ruey (I owe both of you replies to the list, for Steve
agreeing about ES3 capturing paren broken design, Yuh-Ruey on fine
points about instanceof, etc. -- I wanted to drop a note here,
quickly, and catch up next week while I'm off).
The callability of RegExp has been a sore point to some (Garrett
Smith at least sees no benefit), and it led SpiderMonkey, which has
made RegExp instances callable since the last millennium, to follow
the exact letter of ES3 and therefore have typeof /hi/ == "function".
This deviates from other browsers, creating interop problems, and
Firefox 3 (JS1.8) no longer follows ES3 here. For the same reason,
ES4 is changing ES3 to say that typeof x == "function" iff x
instanceof Function for some global object's Function constructor.
Given this back-off on the typeof front, and the more significant
fact that !(/re/ instanceof Function), which we cannot change, I have
to ask: is RegExp callability worth it?
Calling a regexp instead of invoking its .exec method is convenient
enough once you get used to it, but .exec is only five more
characters, and some people (sorry for forgetting exactly who they
are) point out that calling a RegExp might rather be defined as a
call to .test (to avoid creating the returned array -- here again an
implementation can optimize based on the continuation of the call to
the regexp object -- SpiderMonkey does this).
If we keep regexp callability in ES4 as a new convenience, then I
agree we should think about call and apply. People can use
Function.prototype.call, or in ES4 Function.call the static helper,
but you end up passing the regexp twice (as the callee and the |this|
parameter): Function.call(regexp, regexp, input) instead of simply
regexp(input).
This raises the question: since there is only one argument to exec or
test, so why do you ever need to delegate invocation using apply or
call? The only case that comes to mind is a generic programming use-
case where you might have either a function object or a regexp and
you want to .apply it with an argument array. But again, you could
just invoke foo(arg) given foo denoted either a function or a regexp.
True, the most generic program would not want to hardwire argument
count, so would want .apply. But still, the motivation for call and
apply in RegExp.prototype seems weak.
Note that in Steve's example, regex.call(context, str), context
must be a RegExp instance. There's no benefit to using .call
or .apply over just regex(str) if you know you're passing exactly one
(input string) argument.
Summarizing my questions:
- Is regexp callability worth adding in ES4?
- If yes to 1, do we really need .call and .apply?
I'm leaning toward no/no, could be content with yes/no, do not
believe yes/yes based on the above reasoning.
On 22/12/2007, Brendan Eich <brendan at mozilla.org> wrote:
Summarizing my questions:
- Is regexp callability worth adding in ES4?
I would say a tentative no to that. It neither adds anything new nor significantly reduces code size. As you say, re() is only five characters shorter than re.exec. The only real benefit from it that I can see is that it allows generalised functions to call any function-or-regex argument on a string, instead of having to fork the code.
- If yes to 1, do we really need .call and .apply?
I don't think it's needed, except for symmetry with real functions.
(Does this stance make me a hypocrite for being the one who filed the RFE for this feature in Opera?)
(I agree with liorean.)
- Is regexp callability worth adding in ES4?
IMO, no it isn't. Although I do enjoy the convenience of things like array.filter(/regex/) (which works in Firefox today), outside of array iteration methods it typically just saves five characters and is not behavior most people would expect. If I want to be able to pass regexes around as functions and use call and apply on them in the cases where it is helpful, I can implement this functionality myself (my custom methods would likely ignore the context argument ... e.g. RegExp.prototype.call=function(context,str){return this.exec(str);}; ).
- If yes to 1, do we really need .call and .apply?
I don't think it's needed, but the symmetry with real functions seems natural to me (but only if callability were to be preserved, which as noted above is not something I'd argue for).
As for whether test or exec should be the method used if callability is preserved, I think test is what would be desired in far more cases. However, I have grown used to the idea of exec being the default method (since the functionality of all regex and regex-using string methods can be reproduced using exec), and the null and array values returned by exec cast nicely to booleans anyway. So, I don't really care either way.
Brendan Eich wrote:
On Dec 21, 2007, at 7:33 AM, StevenLevithan wrote:
Yes, its easy to pull off oneself, so I don't care much other way.
Still, it seems pretty weird to me to be able to doregex(str)
but notregex.call(context, str)
. This is accentuated when typeof returns "function" for regexes (though it seems ES4 will change this to
"object").Hey Steve, Yuh-Ruey (I owe both of you replies to the list, for Steve
agreeing about ES3 capturing paren broken design, Yuh-Ruey on fine
points about instanceof, etc. -- I wanted to drop a note here,
quickly, and catch up next week while I'm off).
I'm glad you still remember it :)
This raises the question: since there is only one argument to exec or
test, so why do you ever need to delegate invocation using apply or
call? The only case that comes to mind is a generic programming use- case where you might have either a function object or a regexp and
you want to .apply it with an argument array. But again, you could
just invoke foo(arg) given foo denoted either a function or a regexp.
True, the most generic program would not want to hardwire argument
count, so would want .apply. But still, the motivation for call and
apply in RegExp.prototype seems weak.
With currying, I find that use case a moot point. For example, we could do:
foo(regex.exec.bind())
and foo would still be generic. So really, regex invocation is not needed. Speaking of bind (or whatever it's called), I hope the function is memoized, since it might be used a lot.
ES4 proposals include making regexes callable as a function on a single string argument, which serves as a shorthand for calling the regex's exec method. To further extend this idea, what about also including call and apply methods on RegExp.prototype, so that regexes can more easily be used with functional programming? For e.g., if one were to model a "where" function after JS 1.6's Array.prototype.filter like so:
function where (array, func, context) { var results = []; for (var i = 0; i < array.length; i++) { if (func.call(context, array[i], i, array)) results.push(array[i]); } return results; }
...They could then use, e.g., where(["ab", "ba", "a", "b"], /^a/) to return all array elements starting with the letter "a" (["ab", "a"]).
Thoughts? Is this possibly already included in the proposals? (sorry if I missed it)