7

Why do languages demand catch blocks when they aren't needed?

The compiler or parser complains with this code:

try {
    const utils = require("applicationutils");
}

But it is OK with this code:

try {
    const utils = require("applicationutils");
} catch(e) {}

I don't need the catch block.

I'm using JavaScript if it matters.

Update Example Code:

// setting defaults ahead of try - no need for a catch block
var setting = 10;
var myRegEx = "/123/g";
var supportsRegEx2 = false;

try {
    const utils = require("applicationutils");
    setting = 20;
    myRegEx = "/123/gm";
    supportsRegEx2 = true;
}

The long story
I'm working in an browser like environment where new API's are introduced frequently. Some API's are silently introduced, no documentation but available.

If I want to use a new API I can set a minimum-version flag in my manifest. But if I set a minimum then this excludes anyone before this version.

I've received emails from users who have various reasons they are unable to upgrade; some are using previous versions simply because they haven't updated and others because of office politics.

I've known businesses who the last time I've checked are still using IE6. A few times I've seen system requirements increased that excludes previous generation hardware.

I could have found when an API was introduced and check against a version number or I could try to include the class so later I could check a supports flag or check if the class is not null.

Since setting a minimum version would exclude a segment of the audience this way I could support the users who have not updated while still providing users who have updated access to the features using newer APIs.

Approach 1:

const system = require("system");

try {
   const foo = require("foo");
}

function performSomeAction() {
    if (supportsFoo) {
       foo.bar();
    }
}

Approach 2:

const system = require("system");
var supportsFoo = false;

try {
   const foo = require("foo");
   supportsFoo = true;
}

function start() {
    if (foo) {
       // do something
    }
}

In my cases I can't see a catch block being necessary.

Semantics:

For my specific case:

Try to import a class using require() and
set a constant or variable to that class / api
If an error is thrown skip any other code in the try block and continue
If no error the constant or variable will not be null
In the constructor check for not-null and enable features for use

Per a comment below here is test code in JS environment:

var x = function() {
    try {
        console.log("hello")
        throw new Error();
        console.log("world");
    }
    // catch(e) {}
    console.log("After try");
}

// VM373:6 Uncaught SyntaxError: Missing catch or finally after try

ANOTHER USE CASE (5 days later):
FWIW in CSS there is the idea of a progressive enhancement.

Because of the way CSS styles are defined, styles can be defined multiple times and styles of the same name that are added last overwrite values set before it.

So you can have this list of styles like so:

body {
   color: red;
   color: blue;
}

The color will be blue because it is defined last. That's perfectly valid in CSS. It's not right or wrong it's valid.

So this comes in handy when you want to support progressive features without breaking support for earlier browsers:

.slideshow {
    display: flex;
    display: grid;
}

In the style declaration above the browser will use a grid display if it is supported and if not it will use flex display. There is no error thrown for using an incorrect value.

That's the same as:

var element = document.getElementById("label");
element.style.setPropertyValue("flex");

try {
    // if the style is not supported the browser retains the value flex
    element.style.setPropertyValue("display", "grid");
} catch(e){ /* no catch is needed */ }

You could also write the CSS as:

.slideshow {
    display: grid;
}

@supports (display: grid) {
    .slideshow {
        display: grid;
    }
}

The code for that would be:

var element = document.getElementById("label");
element.style.setPropertyValue("display", "flex");

if (CSS.supports("('display:grid')")) {
    element.style.setPropertyValue("display", "grid");
}

In both cases you are defining a variable, attempting to set test / set it to a new value that it may not support.

The first approach is recommended for greatest backwards compatibility:

.slideshow {
    display:flex;
    display:grid;
}

Granted, when setting styles that are not compatible the browser will retain the previous valid values. I'm banking on this knowledge or this information to determine that a catch block is not necessary. This isn't my use case btw. My use case is in the "long story" section.

closed as off-topic by gnat, Jörg W Mittag, BobDalgleish, GrandmasterB, Robert Harvey May 18 at 15:41

This question appears to be off-topic. The users who voted to close gave these specific reasons:

  • "Questions asking for assistance in explaining, writing or debugging code are off-topic here. These can be asked on Stack Overflow if they include the desired behavior, a specific problem or error, and the shortest code necessary to reproduce it in the question (see Minimal, Complete, and Verifiable Example)." – Robert Harvey
  • "Questions asking us to find or recommend tools, libraries, programming languages, resources (including books, blogs, tutorials, and examples), or projects to undertake are off-topic here as they attract opinionated answers that won't have lasting value to others. You may be able to get help in The Whiteboard, our chat room." – gnat, Jörg W Mittag, BobDalgleish, GrandmasterB
If this question can be reworded to fit the rules in the help center, please edit the question.

  • 27
    Could you explain why you want a try without a catch-block? – Pieter B May 17 at 5:06
  • 8
    Can you explain what you want the semantics to be? You have only asked about the syntax so far, but the syntax is the least important part of a programming language. For example: yes, in Ruby, you don't need to use catch together with try. In fact, you can't use catch together with try. But that is because in Ruby, try and catch have nothing to do with each other, and neither of the two has anything to do with exceptions. – Jörg W Mittag May 17 at 6:34
  • 6
    I think he wants it to quietly swallow the exception. The reason there's no shorthand for that is that it's bad practice. – BlueRaja - Danny Pflughoeft May 17 at 7:34
  • 4
    After reading "The long story", this sounds like entirely the wrong approach. If you want to use a new API, it makes sense that it wouldn't be available on old systems. But the way you go about it is by splitting your codebase. You either need to re-implement the functionality yourself for older environments or simply, silently drop the functionality without affecting other things. In both cases that leads to parallel implementations you have to maintain. – VLAZ May 17 at 11:05
  • 3
    @1.21gigawatts: "It allows the parser to continue executing whether the class is available or not" – Again, you are only talking about syntax, when the important thing is the semantics, which you have been asked about numerous times and thus far refused to answer. By the way, the parser doesn't care one bit whether the class is available or not, the AST will be exactly the same either way. You need to specify the semantics of the feature you are looking for. And it is not enough to simply throw out examples without any explanation. You need to properly specify the semantics for … – Jörg W Mittag May 17 at 12:01
60

I don't need the catch block.

But you do need to catch. The behavior of your code with a catch block is to catch any exception, and then forget that it happened. So any exception that tries to pass through will stop, and your code will basically pretend that the try block executed successfully.

So you want a naked try block to act like it catches an exception and ignores it. Here's the thing: a default case is meant to be a common case, one that is useful by many users and not error prone. if doesn't typically require an else because there are many cases where you have nothing to do.

I know nothing about why you want to drop exceptions on the floor and pretend they didn't happen. I'm willing to accept that you have some good justification for doing so. But the fact is, in the general case, it's not a good idea. Most programmers don't want to do it, and there are good arguments to say that it is generally unwise to do this sort of thing.

A good language will let you do something unwise. But a good language will not let you do something unwise by accident. Since the behavior you want is generally unwise, languages tend to make you explicitly request it.

  • 10
    FWIW, if does require an else in Haskell since it's not possible to "do nothing". – immibis May 17 at 4:10
  • 2
    Javas' try-with doesn't require a catch block – XtremeBaumer May 17 at 8:10
  • 3
    @XtremeBaumer but it does have an implicit finally – Caleth May 17 at 8:29
  • 15
    @immibis You're comparing apples and oranges. if in Haskell is an expression. if in most languages is a statement. when is a better analog to the if statement. – Mateen Ulhaq May 17 at 8:56
  • 2
    And the semantics of if in Haskell is more like the tenerary <a> ? <b> : <c> operator in other languages. Or if in Excel. – bdsl May 17 at 12:39
33

As others have pointed out, there are good reasons why a plain try is not allowed in JavaScript, or in most other languages that have the same syntax:

  • It's not even obvious what it should do: for consistency with try...finally, a try with no catch should arguably just let any exceptions through without, well, catching them. But without a catch or a finally block, such a plain try would be useless.

  • Most likely, a try without a catch (or a finally) is just an editing mistake, just like an else without an if. It's a good thing for the parser to notice and inform you of such mistakes.

  • Catching exceptions and ignoring them completely is an unusual and risky thing to do, since it's easy to catch more than you expected. If you really want to do it, it's a good thing that the language at least forces you to be explicit about it.

In particular, note that your example code is kind of fragile and makes it easy to introduce subtle bugs that can be hard to detect and debug, since everything will seem to behave normally. For instance, let's make the following seemingly harmless change to your example:

// setting defaults ahead of try - no need for a catch block?
var setting = 10;
var myRegEx = optimizeRegEx("/123/g");
var supportsRegEx2 = false;

try {
    const utils = require("applicationutils");
    setting = 20;
    myRegEx = optimizeRegEx("/123/gm");
    supportsRegEx2 = true;
}
catch (e) {} // why does JS force me to do this?

Can you see what might go wrong here? I'll give you a moment to think about it...

OK, so what happens if optimizeRegEx() has a bug and crashes when given the input "/123/gm"? That exception will also be caught and ignored, since it happens inside the try block, so you'll never see it! Worse, now your variables will end up in an inconsistent state: setting will be 20 and the applicationutils module will be loaded, but myRegEx will still have its original value and supportsRegEx2 will be false. Have fun debugging that!

So how would you do that safely? Well, the first thing would be to ensure that the variables always end up in a consistent state, at least. One way to do that would be to (re)set them to their defaults in the catch block:

// these will be set inside the try/catch block below
var setting, myRegEx, supportsRegEx2;

try {
    const utils = require("applicationutils");
    setting = 20;
    myRegEx = optimizeRegEx("/123/gm");
    supportsRegEx2 = true;
}
catch (e) {
    // something went wrong, probably require() failed: use fallback defaults instead
    setting = 10;
    myRegEx = optimizeRegEx("/123/g");
    supportsRegEx2 = false;
}

That's a bit better: any exceptions from optimizeRegEx("/123/gm") will still be silently thrown away, but at least all the variables will end up with their default values if that happens.

Even better would be to fix the code to only catch the specific exception thrown when the require() fails. Unfortunately JavaScript makes this harder than it should be, since it doesn't support conditional catch clauses. But we can at least follow the general principle of having as little code as possible inside each try to avoid catching unexpected exceptions, and we can also inspect the caught exception inside the catch block and rethrow it if it's not what we expected to see:

// setting defaults ahead of try
var setting = 10;
var myRegEx = optimizeRegEx("/123/g");
var supportsRegEx2 = false;

// try loading utils, catch and ignore module loading error
var utils = null;
try {
    utils = require("applicationutils");
}
catch (e) {
    if (e.code !== 'MODULE_NOT_FOUND') throw e;  // rethrow any unexpected errors
}

// adjust variables if the module loaded without errors
if (utils !== null) {
    setting = 20;
    myRegEx = optimizeRegEx("/123/gm");
    supportsRegEx2 = true;
}

(Error code test based on this answer on SO.)

Now we're only ignoring those errors that come from the statement that we expect to throw them and which also look like the error we expect. This minimizes the risk of us accidentally ignoring an error that wasn't the harmless one we were expecting, and thus makes the code more robust and easier to debug.


Regarding your actual use case (now that you've described it), it seems to me that what you really want is a wrapper function like this:

function tryRequire(moduleName) {
    try {
        return require(moduleName);
    }
    catch (e) {
        if (e.code === 'MODULE_NOT_FOUND') return null;  // adjust this as needed to detect module loading errors in your environment
        throw e;  // rethrow any unexpected errors
    }
}

This way, you can just do const foo = tryRequire('foo'), and then check whether the module was successfully loaded based on whether foo is null.

  • 2
    Good answer with great examples that demonstrate why ignoring exceptions is a bad idea, and therefore doesn't have a syntax that makes it easier. OP should consider using the advice in this answer to refactor his code. – LordOfThePigs May 17 at 8:06
  • 4
    "easy to introduce subtle bugs" funny you predicted that - OP added an example of what he wants to do and has exactly this subtle bug you talked about here. The intention is to try and load a module "foo", if successful, to set supportsFoo to true. However, that variable is declared as const, so re-assigning it will throw an error...which OP wants to implicitly and silently ignore. Thus the code would work but would always claim that supportsFoo is false, even if loading the module succeeded. Case in point why you want to be specific and explicit with error handling. – VLAZ May 17 at 11:15
  • @VLAZ Yes. Ilmari makes a good point. But I would have caught that during normal development during the part where you test code immediately after you write it. Or the compiler would have flagged reassignment of a const. All development can be said to be, "easy to introduce subtle bugs." but yes, good points. I've updated the code to use var instead of const. – 1.21 gigawatts May 17 at 19:34
  • 5
    @1.21gigawatts “I would have caught that during normal development” - I have found that every time I have this mindset I introduce a bug that is caught in production in a spectacularly embarrassing fashion. – SethMMorton May 18 at 19:34
9

Many languages permit: try { ... } finally { ... } or some variant. Take C# as an example. There are plenty of others.

But there is no point in a try { ... }. It has no meaning without an associated:

  • catch(...) { ... }
  • finally { ... }
  • catch(...) { ... } finally { ... }.
  • catch(...) { ... } catch(...) { ... } ... catch(...) { ... }.

The use case you are highlighting is actually very bad code.

If you are expecting an error, and you do nothing about it, your design is fundamentally flawed.

You could say that this syntax is working as expected. Because it is irritating you enough to look for a solution.

The solution is to either get rid of the try altogether, or figure out how to handle the error.

  • 2
    "There is no point to a try {}". Hmm... "There's never a use case for it and so it's bad code?" You don't know enough about the situation to know that. Should every if statement have an else statement? The compiler throws errors when you encounter a class you want to import that doesn't exist but you have to import it to use it. I'll add more code. – 1.21 gigawatts May 17 at 1:34
  • 3
    try {} finally {} won't suppress the exception though, it'll get raised in the caller. It seems the OP wants to suppress all exceptions. – whatsisname May 17 at 1:38
  • 7
    @1.21gigawatts: Exactly what behavior do you want, then? There are only two things that I could imagine happening in this case. The exception gets caught, consumed and dropped; or the exception passes through your code and out into the caller like normal. – Nicol Bolas May 17 at 1:46
  • 1
    @1.21gigawatts the reason we don't know about the situation because you've not explained. There is no logical reason to have try without any handling there - you may as well drop the try block entirely. After all, you don't want to specify what happens when an exception crops up, so the most constistent thing a computer can infer from "try running this code. The are no special instructions for if it fails" is to run the code and if it fails to just propagate the exception as normal. Which makes the code pointless. Similar to how if (true) const val = 1 in JS with no block is pointless. – VLAZ May 17 at 7:41
  • 2
    "Should every if statement have an else statement?" That's not really a good analogy. Look at the catch block as corresponding to the "then" part of an if statement. A try without a catch is like writing if (predicate) {}. – Zano May 17 at 8:18
4

What would that mean?

I would expect:

try {
    const utils = require("applicationutils");
}

To mean the same thing as:

const utils = require("applicationutils");

try/catch/finally is a well understood pattern, used in a lot of different languages. Exceptions by their very nature imply that there is a recovery handler somewhere else.

It sounds like what you are looking for is VB’s On Error Resume Next, which catches and ignores errors on a line by line basis. Today this is generally considered a bad practice, because it means that there is no actual error handling going on — flow continues as if the previous line had succeeded, which can lead to escalating the level of garbage/damage that is done by bad input, instead of halting or eliminating just the bad input.

The try/catch pattern is useful, because it can segment your algorithm into parts where you can fail but recover, and parts where there is no recovery after failure. And in the event that there is no recovery after failure, it is that part that cannot continue not the rest of the application.

If you have a need for this in js, you could get the same effect, by creating a function that takes a function as it’s argument, wraps it in a executes it inside a try catch block where it just sets an error object. You could then use lambda expressions for any line you would like to resume next on.

var err = {}
var ex = function(action){
             try {
                 action()
             }
             catch (e) {
                 err=e
             }
         }
 var i =5
 ex(t=> alert(i.foo()))
 console.log(i)
 console.log(err.message)

Although it won’t work with lines that declare and initialize variables.

  • 1
    Look at the example on the question. OP does not want setting, myRegEx and supportsRegEx2 to be modified if require fails. It is not a simple On Error Resume Next. It would be more like On Error Goto Label. Addendum: ultimately what OP wants is to omit catch(e) {} but have it behave exactly as if it were there. – Theraot May 17 at 5:00
  • Of note: that block scope is important because of the semantics of const. Notably, const x = require("anything") will result in x as the module after execution, but try{const x = require("anything")}catch(e){} will result in x being undefined after execution, since const is block-scoped - the fact that you left a block is important (it also depends on whether you're in strict mode). – Delioth May 20 at 20:36
  • I refer to the version of May 17 at 1:39, your answer is date at May 17 at 3:15. – Theraot May 29 at 13:55
3

No, no language support this syntax. The closest I know is VB's ON ERROR RESUME NEXT which kind of does what you want: It ignores any exception and just continue on the next line.

So why doesn't languages support this? Because it is very vare rare you need it in a well-designed program. Usually a missing catch or finally would indicate the programmer forgot it, so the syntax is a safeguard.

In the very rare case where you want to just ignore exceptions you can use an empty catch block with a comment explaining why you ignore exceptions here. This signals the intentions much better. If you are doing something really uncommon and potential dangerous, then it is good style to communicate clearly in the code that you know what you are doing (and why).

2

Swift allows try without catch. There are four ways in Swift to handle exceptions: 1. The usual way with try and catch. 2. By using "try?" on a method call returning a result of type T, which means you get a result of type Optional, and the result is nil if the call did throw an exception. 3. By using "try!", which means your application crashes if an exception is thrown, and 4. by making a call in a method that is itself throwing.

Not the answer you're looking for? Browse other questions tagged or ask your own question.