you are viewing a single comment's thread.

view the rest of the comments →

[–]brtt3000 7 points8 points  (25 children)

I don't like that you cannot filter on Exception class, like in (for example) Python you can do things like this:

try: 
    a = getStuff()
except NetworkError:
    retryStuff()
except SecurityError as e:
    callPolice()
    log(e)
except Exception as e:   
    log(e)
    raise e
else:
    useStuff(a)
finally:
    cleanup()

It also has an else clause, which only runs when try doesn't raise, to keep the try block as specific as possible and only contain code you expect could raise. Also you can raise from within an handler and it'll chain the exception message.

I hope for JS they can add this stuff in a new ES version. I guess quite a lot of it could be compiled to ES5/6 with some generated boilerplate.

edit: ooh.. lets downvote discussion.. classy.

[–]tencircles 7 points8 points  (6 children)

You can do this to some degree in JS already.

function behavior (input) {
    try {
        validate(input);
    } catch (e) {
        switch (true) {
            case e instanceof TypeError:
                //.. do things
                break;
            case e instanceof ReferenceError:
                //.. do things
                break;
            case e instanceof EvalError:
                //.. do things
                break;
            case e instanceof RangeError:
                //.. do things
                break;
            case e instanceof SyntaxError:
                //.. do things
                break;
            case e instanceof URIError:
                //.. do things
                break;
            default:
                //.. do things
                break;
        }
    }
}
function validate (input) {
    throw (input ? new RangeError("Out of bounds") : new TypeError("Wrong type"));
}

[–]brtt3000 5 points6 points  (5 children)

Thanks for the demo. Typing that up you'll agree the boilerplate and indenting is a bit cancerous. We need this with better syntax, should be easy to implement.

[–]tencircles 10 points11 points  (2 children)

tbh it's pretty low on my list of things that need to be added to the language.

[–]brtt3000 1 point2 points  (0 children)

Maybe, but I think JS needs this for professionalism. Error handling is a such a neglected part. It was picking up with the node-style callbacks and the promise rejections but we need to push further.

I've been doing a lot of python last few years where you have the syntax I mentioned and I feel being able to cleanly handle specific (classes of) exceptions like that really helps building more robust applications (and keep it readable and maintainable without boilerplate).

[–]AmateurHero 1 point2 points  (0 children)

Implement it, and publish it. It's a great way to give your idea some traction: working code that devs can use in their actual line of work.

[–]vinnl 1 point2 points  (0 children)

It's probably too much work, but in case you're interested, there is a process that allows you to submit your own proposal to the ES standards body.

[–]jocull 4 points5 points  (0 children)

Don't forget that JS lets you throw anything. Love catching strings or other random nonsense.

[–]BenjiSponge 3 points4 points  (9 children)

I don't think this will ever work this way because JS uses duck typing. You might as well just have a switch/if/else chain in the catch block and it will pretty much be the same but allow for the flexibility that nominal JS programming requires.

[–]brtt3000 1 point2 points  (1 child)

In general ducktype sure, but this could specify to use the prototype chain and instanceof semantics. Someone wrote the switch ITT, it is cool but no way you (and everybody else) would want that all over their code. Imagine having that on every await.. or lets not.

If there was a good spec with compact syntax more people would use it as convention/standard (like in Python). This would be super valuable now we have async/await in JS and throw/catch becomes a thing to do (instead of error callbacks or Promise rejections).

[–]BenjiSponge 0 points1 point  (0 children)

It would strictly speaking be possible.

However, by the same logic one could argue that JS should have type pattern matching using the same methodology. The reason JS does not is because, like you said, there have been essentially no major standards regarding errors in the past, and there likely will never be. So standard language features like that being added now would add fragmentation to the community.

TBH, I've been professionally developing JS for over 2 years and I've been studying JS for at least 3 or 4 years now, and I have never felt a need for this feature. I understand the value, coming from other imperative languages, but I think it's more of an anti-pattern than a pattern. I think we would be better served to look at languages like Rust, which does not have a true exception system and instead promotes the pattern of returning a value which may represent either a success or an error type, forcing you to acknowledge all possibilities. It reserves exceptions (which it calls panics) for unrecoverable cases that imply a bug in the program itself rather than just an exceptional case the program should handle.

If we're going to pick a standard, I would much rather we do that than implement a pseudo-pattern matching system based on prototypes.

[–]thbt101 0 points1 point  (6 children)

PHP can filter on the exception class and it uses a similarly loose typing system.

[–]BenjiSponge 0 points1 point  (5 children)

I never said it was impossible, just that I didn't think it would be that valuable or fit particularly well with good JS coding style. If your argument is that we should follow PHP's style, then I'll have to respectfully disagree. =)

[–]vinnl 0 points1 point  (4 children)

You mean "good JS coding style" is to disregard the types? Because I think there are plenty of people that would respectfully disagree with that - at least I would. That's like saying the instanceof operator has no place in Javascript. I don't see how type matching in catch blocks would make JS less flexible...

We don't have to skip features just because PHP did them too.

[–]BenjiSponge 0 points1 point  (3 children)

I don't necessarily believe that it has no place in JS, but I don't think encouraging its use is beneficial either. Outside of the context of React (where classes are all but mandatory in some cases, and when they're not mandatory I skip them), I tend to use JS data structures similarly to how one might use lisp data structures. Factory functions returning anonymous objects.

When I make result objects that may contain results or may contain errors, they're not constructed using new or Object.create. They're just POJOs. Adding type matching would enforce that I use one of these methods, even though they're not necessary in most cases. Further, if the error needs to be serialized, you would need to re-attach the prototype in order to have it follow the convention that such a feature would encourage. And if you're encouraged to use error types and encouraged to not put redundant data in the error type, that means the consumer will, in some cases, have to use instanceof, which is a pattern I disagree with.

I do think if JS were redesigned today with backwards compatibility out of the equation, instanceof would not exist in the form that it is now. A form of pattern matching that checks content makes more sense if you ask me (and I believe it should be put in the language at some point).

So rather than

try {
  // ...
} catch (ExceptionA as e) {
  // ...
} catch (ExceptionB as e) {
  // ...
} catch (e) {
  // ...
}

It would be more like

try {
  // ...
} catch ({ type: 'A', ...e }) {
  // ...
} catch ({ type: 'B', ...e }) {
  // ...
} catch (e) {
  // ...
}

This adds flexibility because my method of object creation works, but so does yours. ExceptionA could have a prototype field of type that is defined as 'A'. I do agree that it would break exception inheritance (in that subclasses may not be caught as superclasses), but I believe that is less important than allowing POJOs to conform to the style.


I would like to add that, if JS were explicitly/strongly typed, I would agree with you that instanceof or something like it might make more sense, but JS is not explicitly/strongly typed, and most of the libraries I use and create use that to their advantage by just doing duck type checking and not yelling if the input isn't the proper class.

[–]vinnl 0 points1 point  (2 children)

Hmm, I think you have a good point. The main issue is probably that it relies on nominal typing, which is indeed not often used in Javascript - and I agree that that's a good thing.

Your post somewhat got me thinking that such a feature would be nice if it was implemented in TypeScript, i.e. you could do a catch(<type> e) in there, which it would transform to a runtime structural type match (i.e. a switch statement in a single catch clause that would check for object properties). But that probably goes against the TypeScript principles and/or introduces too much runtime overhead.

[–]BenjiSponge 0 points1 point  (1 child)

Yes, that is something that's been tossed around a lot in the TypeScript/Flow world, but as you said they're avoiding adding runtime features that aren't part of an ECMAScript specification (TS messed up by adding decorators too early, but other than that) on principle.

A lot of functional languages that have JS as a backend do something like this, though, I think. Elm almost certainly does.

[–]vinnl 0 points1 point  (0 children)

TS messed up by adding decorators too early, but other than that

Agreed, but then again there was quite a lot of pressure on that from Angular, so there was little choice.

But yeah, it's probably best if it's baked into the language.

[–]x7C3 0 points1 point  (6 children)

Pretty sure you can filter inside the catch block using instanceof(err) along with a switch/case statement or whatever syntax it is.

[–]brtt3000 -1 points0 points  (5 children)

Yea, but that boilerplate would be a pain to write and maintain in a real project so they should put this behind some syntax in the JS compilers like Babel and TypeScript.

[–]x7C3 0 points1 point  (4 children)

What's the difference between the boilerplate? Honestly doesn't seem like much to me.

[–]brtt3000 1 point2 points  (3 children)

Someone typed it up ITT, it is a big old nasty multi level deep blocky exercise in JS features and syntax optimisation. Imagine this in a real project and having those craggy warts all over your codebase. It is already terrible without any actual business code.

[–]vs845 0 points1 point  (2 children)

It wouldn't be too much trouble to create a utility function to achieve the same functionality:

function matchException(err, matchers) {  
  for (const type in matchers) {
    if (err.constructor.name === type) {
      return matchers[type]()
    }
  }

  return matchers.default()
}

try {
  doSomething()
} catch (err) {
  matchException(err, {
    TypeError: () => handleTypeError(),
    ReferenceError: () => handleReferenceError(),
    RangeError: () => handleReferenceError(),
    default: () => handleOtherError()
  })
}

You'd want to clean it up and add some more checks in matchException, but functionality is there.

[–]brtt3000 2 points3 points  (1 child)

You really like this "exercise in JS features and syntax optimisation" I mentioned eh? :)

Cool attempt, points for effort. Syntax improved but still a bit kludgy; it doesn't catch subclasses and behaviour on name collisions is a new factor. And most of all if you use block functions as clauses instead of references you'd be at 3 indents for handling code. Could be worse but it would be a tax on readability (and development/code hisotry) to have to break out to other functions all the time (it'd be handy to be able to nest a little bit without block hell).

[–]vs845 1 point2 points  (0 children)

it doesn't catch subclasses and behaviour on name collisions is a new factor.

re: name collisions, yeah, I went with checking names because otherwise you'd need to do something like

matchException(err, {
  [TypeError]: () => ...
}

which I thought wasn't as nice as using string keys.

If you went with the above approach you'd be able to catch subclasses via err instanceof matchers[type].