Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upAllow set default value to CatchParameter #1121
Comments
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
chicoxyzzy
Feb 26, 2018
Contributor
My proposal is to change
CatchParameter[Yield, Await]:
BindingIdentifier[?Yield, ?Await]
BindingPattern[?Yield, ?Await]
to
CatchParameter[Yield, Await]:
SingleNameBinding[?Yield, ?Await]
BindingPattern[?Yield, ?Await]Initializer[+In, ?Yield, ?Await]opt
or
CatchParameter[Yield, Await]:
BindingIdentifier[?Yield, ?Await]Initializer[+In, ?Yield, ?Await]opt
BindingPattern[?Yield, ?Await]Initializer[+In, ?Yield, ?Await]opt
I'll be happy to file PR here and in test262 repo if this proposal sounds ok
|
My proposal is to change
to
or
I'll be happy to file PR here and in test262 repo if this proposal sounds ok |
chicoxyzzy
changed the title from
Allow set default parameter to CatchParameter
to
Allow set default value to CatchParameter
Feb 26, 2018
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
chicoxyzzy
Feb 26, 2018
Contributor
To be clear, this problem was reported in Babel and it was caused by 3rd party library (regenerator in this case). Since it already possible to catch any object or primitive value, it looks weird that it's possible to use destructuring and default parameters inside it, but impossible to set default value for CatchParameter.
|
To be clear, this problem was reported in Babel and it was caused by 3rd party library (regenerator in this case). Since it already possible to catch any object or primitive value, it looks weird that it's possible to use destructuring and default parameters inside it, but impossible to set default value for CatchParameter. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
littledan
Feb 28, 2018
Member
I can see the consistency argument for this change, but I'm wondering--when would you actually want to throw undefined?
|
I can see the consistency argument for this change, but I'm wondering--when would you actually want to throw |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
chicoxyzzy
Feb 28, 2018
Contributor
@littledan personally I don't want to throw undefined or any other primitive or object except real Error :) This may happen in 3rd party libraries.
|
@littledan personally I don't want to throw |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
allenwb
Feb 28, 2018
Member
This was an intentional design decision. Because the expectation is there should always be some object coercible value (ie, not undefined or null) that was thrown.
If you want to explore changing something, how about exploring to refusing to throw undefined.
WRT changing CatchParameter, I don't think we should do it. Is somebody really wants to restructure a catch parameter and are concerned about null or undefined they can move the destructuring into the body of the catch clause.
|
This was an intentional design decision. Because the expectation is there should always be some object coercible value (ie, not undefined or null) that was thrown. If you want to explore changing something, how about exploring to refusing to throw undefined. WRT changing CatchParameter, I don't think we should do it. Is somebody really wants to restructure a catch parameter and are concerned about null or undefined they can move the destructuring into the body of the catch clause. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ljharb
Feb 28, 2018
Member
can someone clarify: is it “a default value in the catch binding” that’s currently disallowed, or only in the presence of destructuring, or is it destructuring in the catch binding that’s currently disallowed?
|
can someone clarify: is it “a default value in the catch binding” that’s currently disallowed, or only in the presence of destructuring, or is it destructuring in the catch binding that’s currently disallowed? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ljharb
Feb 28, 2018
Member
(Separately, even if you can’t throw undefined, you’ll always be able to await Promise.reject() which causes an undefined to be thrown; preventing throwing undefined i suspect is a nonstarter)
|
(Separately, even if you can’t throw |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
allenwb
Feb 28, 2018
Member
It's a BindingIdentifier or a BindingPattern, neither of which may have a initializer.
Why in the world does await Promise.reject() throw undefined. It sounds like we've just made catching undefined a normal expectation and that seems like a significant change to how exceptions had previously been used in the ES specification.
|
It's a BindingIdentifier or a BindingPattern, neither of which may have a initializer. Why in the world does |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
getify
Feb 28, 2018
Contributor
A couple use cases for throwing undefined:
-
For awhile in the pre/early days of ES6, some transpilers used a
try..catchhack for transpilingletblock-scoped declarations. For example, my let-er project:{ let x; console.log(x); x = 1; console.log(x); }became:
try { throw undefined; } catch (x) { console.log(x); x = 1; console.log(x); }
-
If you have multiple paths that can throw exceptions, and some of those paths are only to "escape" (e.g., break out of) a
tryblock:try { var x = foo(); var y = bar(x); if (y < 10) throw undefined; // kinda like break; or return; var z = baz(y); } catch (err) { if (err != undefined) console.log(err); }
Both of these are uses that I've actually done in real production code. May not be "ideal" or "recommended", but I'm quite certain that throw undefined is not unprecedented, and can't just be dismissed.
|
A couple use cases for throwing
Both of these are uses that I've actually done in real production code. May not be "ideal" or "recommended", but I'm quite certain that |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ljharb
Feb 28, 2018
Member
Catching undefined has always been possible; and I’d say with the introduction of Promises that can reject with a nullish reason, that was permanently cemented in ES2015 (due to the intended parallels between rejection and throwing)
|
Catching undefined has always been possible; and I’d say with the introduction of Promises that can reject with a nullish reason, that was permanently cemented in ES2015 (due to the intended parallels between rejection and throwing) |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bakkot
Feb 28, 2018
Contributor
While it's certainly possible, I'm not sure it's a case which merits having explicit grammar support to handle.
|
While it's certainly possible, I'm not sure it's a case which merits having explicit grammar support to handle. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ljharb
Feb 28, 2018
Member
From the spec side it’s explicit support, sure, but from the user side it looks like an explicit and surprising restriction imo.
|
From the spec side it’s explicit support, sure, but from the user side it looks like an explicit and surprising restriction imo. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
littledan
Feb 28, 2018
Member
@getify for 2., would labeled blocks work? See the MDN article for an example.
|
@getify for 2., would labeled blocks work? See the MDN article for an example. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
getify
Feb 28, 2018
Contributor
@littledan not in this particular case because what you want is the code in the catch to have a chance to run.
|
@littledan not in this particular case because what you want is the code in the |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
littledan
Feb 28, 2018
Member
I mean, you could decouple the breaking-out and the catch block as follows, eliminating the need for the conditional and use of undefined as a sentinel; might be more straightforward:
try {
label: {
var x = foo();
var y = bar(x);
if (y < 10) break label;
var z = baz(y);
}
}
catch (err) {
console.log(err);
}|
I mean, you could decouple the breaking-out and the catch block as follows, eliminating the need for the conditional and use of try {
label: {
var x = foo();
var y = bar(x);
if (y < 10) break label;
var z = baz(y);
}
}
catch (err) {
console.log(err);
} |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
getify
Feb 28, 2018
Contributor
@littledan yes that could work in certain circumstances. but any code after the labeled block (aka any sentinel conditional code) should only run in the break case and not in the normal flow case, so that has to be accounted for in some way. also, adding extra blocks (esp if there's more than one) has the potential extra complication of juggling block-scoped declarations.
|
@littledan yes that could work in certain circumstances. but any code after the |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
chicoxyzzy
Mar 2, 2018
Contributor
undefined couldn't be avoided for some 3rd party code and it is possible to throw undefined since ES3, also I don't see any strong arguments against of Initialiser in CatchParameter. Sure, one can use check inside of body of catch clause but there is no single reason for such restriction.
Since this code is valid
try {
// ...
} catch ({foo = 'bar'}) {
// do something with foo
}I don't see any reasons why one should write
try {
// ...
} catch (e) {
const foo = typeof e === 'undefined' || typeof e.foo === 'undefined'
? 'bar'
: e.foo;
// do something with foo
}instead of just
try {
// ...
} catch ({foo = 'bar'} = {}) {
// do something with foo
}|
Since this code is valid try {
// ...
} catch ({foo = 'bar'}) {
// do something with foo
}I don't see any reasons why one should write try {
// ...
} catch (e) {
const foo = typeof e === 'undefined' || typeof e.foo === 'undefined'
? 'bar'
: e.foo;
// do something with foo
}instead of just try {
// ...
} catch ({foo = 'bar'} = {}) {
// do something with foo
} |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
littledan
Mar 2, 2018
Member
Honestly it's hard for me to understand when it would be useful to use destructuring in a catch binding, so I'm having trouble making a mental model to weigh @chicoxyzzy 's vs @allenwb 's logic. Could you give an example of a case you'd want to use destructuring with an object literal in a catch binding?
|
Honestly it's hard for me to understand when it would be useful to use destructuring in a catch binding, so I'm having trouble making a mental model to weigh @chicoxyzzy 's vs @allenwb 's logic. Could you give an example of a case you'd want to use destructuring with an object literal in a catch binding? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ljharb
Mar 2, 2018
Member
Destructuring works with any object including an error instance; I’ve used it for destructuring out a message, stack trace, and error code.
|
Destructuring works with any object including an error instance; I’ve used it for destructuring out a message, stack trace, and error code. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
getify
Mar 2, 2018
Contributor
@littledan @ljharb I have too... especially useful to apply defaults to properties of an Error object that may not be present in all engines, like stack, etc.
|
@littledan @ljharb I have too... especially useful to apply defaults to properties of an Error object that may not be present in all engines, like |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
littledan
Mar 2, 2018
Member
OK, this makes perfect sense--you want to read these properties of the object through destructuring, but default to an empty object if a library you're calling out to throws null or undefined. Did you consider that case when deciding against supporting it, @allenwb ?
If we want to do this spec change, I wonder if we should just get rid of the CatchParameter production and use BindingElement instead, directly in the grammar of Catch. Would that do something different from your second proposal, @chicoxyzzy ? Did you have a particular reason why the first alternative might be preferred?
|
OK, this makes perfect sense--you want to read these properties of the object through destructuring, but default to an empty object if a library you're calling out to throws null or undefined. Did you consider that case when deciding against supporting it, @allenwb ? If we want to do this spec change, I wonder if we should just get rid of the CatchParameter production and use BindingElement instead, directly in the grammar of Catch. Would that do something different from your second proposal, @chicoxyzzy ? Did you have a particular reason why the first alternative might be preferred? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
allenwb
Mar 2, 2018
Member
When this was discussed, I think everybody assume that the exception parameter would always be object coercible. Destructuring of exception parameter is definitely useful and I'm sure that most people aren't going to add a ={} just in case. But I don't think it would hurt anything to allow it. The people who don't expect it will still get an error.
I believe all you need to do is redefine CatchParameter as:
CatchParameter : FormalParameter
My recollection is that the only reason it wasn't defined that way in ES2015 was to excluded the default value case.
|
When this was discussed, I think everybody assume that the exception parameter would always be object coercible. Destructuring of exception parameter is definitely useful and I'm sure that most people aren't going to add a I believe all you need to do is redefine CatchParameter as: CatchParameter : FormalParameter My recollection is that the only reason it wasn't defined that way in ES2015 was to excluded the default value case. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
chicoxyzzy
Mar 3, 2018
Contributor
I think CatchParameter : FormalParameter change should solve everything
Should I create new proposal repo and find champion for it or just create PRs to ecma262 and test262 repos for review and further discussions?
Maybe it's a good topic to discuss on March meeting?
|
I think Maybe it's a good topic to discuss on March meeting? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ljharb
Mar 3, 2018
Member
Maybe we should start with a PR, and we can see if it gains consensus in March?
|
Maybe we should start with a PR, and we can see if it gains consensus in March? |
chicoxyzzy commentedFeb 26, 2018
•
edited
There is some inconsistency between
CatchParameterandBindingElement(i.e. function parameters).Example from babel/babel#7434:
This is Syntax Error according to current spec grammar, but are there any reasons why assigning default value is not allowed here while destructuring (and default parameters inside of it) work fine?