Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

try/else proposal #745

Open
ghost opened this issue Jun 30, 2015 · 10 comments
Open

try/else proposal #745

ghost opened this issue Jun 30, 2015 · 10 comments
Labels

Comments

@ghost
Copy link

ghost commented Jun 30, 2015

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.

@ruby-random
Copy link

I am the OP, sorry for using wrong account, forgot to return to the one which is mine.

@ruby-random
Copy link

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.

@ruby-random
Copy link

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.

@ruby-random
Copy link

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
}

@dead-claudia
Copy link
Contributor

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.

@kkirby
Copy link

kkirby commented Jul 8, 2015

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.

@ruby-random
Copy link

@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.

@ruby-random
Copy link

@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 :)

@vendethiel
Copy link
Contributor

We have for/else, and I prefer our semantics to python's. :)

@phanimahesh
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants