`try/else` proposal
Think of some procedure to search for config file in couple of locations, in order. It would be something like this:
find-config = (...locations) ->
for filename in locations
try
return new Config fs.read-file-sync filename, encoding: \utf8
throw new Error "Could not find configuration file"
And to be used like this:
find-config do
"${process.env['HOME']}/.foobarrc"
'./etc/foobar.conf'
'/etc/foobar.conf'
But if we add a new requirement for config to fail on incorrect config files, so new Config can now throw if could not parse, code becomes uglier:
find-config = (...locations) ->
for filename in locations
try
data = fs.read-file-sync filename, encoding: \utf8
catch
continue
try
return new Config data
catch
throw new Error "Malformed configuration file #{filename}: #{e}"
throw new Error "Could not find configuration file"
If there was an else construct, it could be written like this:
find-config = (...locations) ->
for filename in locations
try
data = fs.read-file-sync filename, encoding: \utf8
else
try
return new Config data
catch
throw new Error "Malformed configuration file #{filename}: #{e}"
throw new Error "Could not find configuration file"
Which results in more compact and readable code, and works much better when there is no option to continue a loop. So, I propose to add optional else branch to try/catch/else/finally construct, which should execute when exception didn't happen, instead of catch clause and before finally.
I am the OP, sorry for using wrong account, forgot to return to the one which is mine.
So, following code:
try
TRY-BODY
catch EXC-EXPR
CATCH-BODY
else
ELSE-BODY
finally
FINALLY-BODY
Should compile to:
// these are hoisted to the top of scope, and are common for all `try` within scope
var canary$ = new Object();
var ec$;
var EXC-EXPR;
// on each `try`
ec$ = canary$;
try {
TRY-BODY
} catch (e$) {
EXC-EXPR = ec$ = e$;
CATCH-BODY
} finally {
if (ec$ === canary$) {
ELSE-BODY
}
FINALLY-BODY
}
Separate ec$ variable is required so EXC-EXPR can be deconstructing assignment or anything at all, like it is now. canary$ is unique and can't be thrown (unlike null or undefined), so it can be checked against for whether it had changed or not.
canary$ and ec$ only appear if there is else clause. When there is no else, there is no need in those, and JS output should be the same as in current version.
Or, as @vendethiel suggested, just assign thrown$ = true; inside catch clause:
// these are hoisted to the top of scope, and are common for all `try` within scope
var thrown$;
var EXC-EXPR;
// on each `try`
thrown$ = false;
try {
TRY-BODY
} catch (e$) {
thrown$ = true;
EXC-EXPR = e$;
CATCH-BODY
} finally {
if (!thrown$) {
ELSE-BODY
}
FINALLY-BODY
}
I find this a little confusing to read (even though I understand what you're talking about). else reads too similarly semantically to catch in exception handling, especially coming from a functional programming background. Also, IMHO it would be better to be more explicit here.
And another thing to take a look at: recent discussion to remove some of the unnecessary sugar. For what it's worth, that alone has increased skepticism for new features.
I'm not sure if it matters, but I +1 this idea and prefer the thrown$ = true method over the use of comparing against an Object.
@impinball try/else syntax is pretty straightforward for Python users, as Python provides else clause for most flow control constructs (if, try, while, for etc.) to indicate "successful, non-breaked, non-errored completion". However, as to my knowledge, only if/else and try/else are widespread.
@kkirby yes, thrown$ is the way to go. I don't know what was in my mind when I suggested sentinel/canary object approach, maybe I just forgot that new variables can be introduced :)
We have for/else, and I prefer our semantics to python's. :)
I can't decide whether I Iike LS's for .. else or python's. Both are good, for their own usecases. If python used the keyword nobreak instead, I would have liked a combination of both. for .. nobreak .. else would be super awesome.