<p>(I was not aware of this site and asked, by mistake, <a href="https://cs.stackexchange.com/questions/168781/exception-handling-catch-without-explicit-try">a similar question on cs.stackexchange</a> as well. It is <a href="https://langdev.stackexchange.com/questions/3911/exception-handling-is-one-exception-type-sufficient">related to this question</a> on this site.)</p>
<p><strong>Context</strong></p>
<p>I'm working on a new general-purpose programming language. Exception handling is supposed to be like in Rust, but simpler. I'm specially interested in feedback for exception handling (throw, catch).</p>
<p>I think, same as in Swift and Rust (and Go), exception should be handled immediately, and if not, either the program must stop, or the function must throw an exception. So, no undeclared exceptions. This is what &quot;<code>catch</code> is needed, or the method needs <code>throws</code>&quot; means.</p>
<p><strong>Question</strong></p>
<ul>
<li>The <code>catch</code> catches all exceptions that were thrown within the scope. I argue there is no need for <code>try</code>, because try would requires (unnecessary, in my view) indentation, and messes up diffs. <a href="https://cs.stackexchange.com/questions/168781/exception-handling-catch-without-explicit-try">In Ruby, the equivalent of <code>try</code> is optional as well</a>. Are there other languages like that? I didn't find any. Is there a problem with this idea?</li>
</ul>
<p><strong>Source Code Example</strong></p>
<p><code>throw</code> throws an exception. <code>catch</code> is needed, or the method needs <code>throws</code>:</p>
<pre><code>fun factorial(x int) int throws
    if x &gt; 20
        throw exception('Value too large')
    if x &lt;= 1
        return 1
    f := factorial(x - 1)
  return x * f

fun main()
    i := 0
    while i &lt;= 30
        println('Factorial of ' i ' is ' factorial(i))
        catch e
            println('Factorial of ' i ' resulted in ' e.message)
        i += 1
</code></pre>

<error-handling><exceptions>
Exception handling: 'catch' without explicit 'try'
CC BY-SA 4.0
2024-06-27T11:13:00.637Z
Rust has [a unsable feature for a try block](https://doc.rust-lang.org/beta/unstable-book/language-features/try-blocks.html)
mousetail 'he-him'
CC BY-SA 4.0
2024-06-27T15:07:19.653Z
You might find [this article about the error handling from the Midori project](https://joeduffyblog.com/2016/02/07/the-error-model/) interesting. For instance, it allowed a definition of `int Foo() throws { ... }`, and a call-site sugar of `Result<int> value = try Foo() else catch;`
IMSoP
CC BY-SA 4.0
2024-06-27T15:24:56.893Z
What *is* a scope in your language? A function? A statement? Something else? What happens when `catch` is in the middle of a block - does its scope span the entire block, including after it? Does your language have the means to explicitly create a new scope?
MisterMiyagi
CC BY-SA 4.0
2024-06-27T15:52:11.163Z
I have now updated the code example. A scope is a function, or block (e.g. `if` or `while` block). If there is a `catch` in the middle, I didn't think about that, but I assume technically it is still the same scope, the same as if there is an `if` statement in the middle of a block. You can create a new (nested) scope by using, for example `if true`.
Thomas Mueller
CC BY-SA 4.0
2024-06-27T15:56:54.370Z
The `if true` option means you get *two* levels of indentation where `try`/`catch` would have one, since the `catch` block must be indented one level further than the `if` block. Besides, idioms (like writing `if true` where the programmer's intention is `try`) generally arise from users working around flaws or oversights in the language's design, so it doesn't bode well if an idiom like that is the "officially recommended" way of doing something.
kaya3
CC BY-SA 4.0
2024-06-27T16:09:43.663Z
@kaya3 there is no `try`, so no, there is only one level. I don't see the reason to use `if true` in practise... you extend the scope slightly, I don't see much harm. I also don't see much harm in making the uncommon cases slightly harder, if this simplifies the common case.
Thomas Mueller
CC BY-SA 4.0
2024-06-27T17:19:03.043Z
It is obvious that a catch can be attached to arbitrary blocks, so the question would not be jutsified. I vote to close because in its current form the question is more of an answer with a lot of opinion. Also, the logic is inconsistent. And the question contains unrelated details.
feldentm
CC BY-SA 4.0
2024-06-27T17:45:11.627Z
@ThomasMueller As I said, you have the indentation for the `if` block, and then the `catch` block is indented further than that. If you had `try` instead of `if` then the `catch` block would be at the same level of indentation, instead of nested. As for which is the "common" case, your way is only correct if a `try` statement would be the first statement in its block, which in my experience is not that common.
kaya3
CC BY-SA 4.0
2024-06-27T18:11:10.343Z
@kaya3 yes the catch is indented, I understand now what you mean. As for "is try the first statement in the block?", I tested this and found (in Apache Jackrabbit) that there are 2566 "try" blocks, and in 1053 the previous line contains "{"; in 189 cases it contains "}". Meaning that in many cases, "try" is indeed the first statement in a block. For this project. I didn't investigate much further (there are many empty lines, and I didn't check if other lines might or might not throw other exceptions).
Thomas Mueller
CC BY-SA 4.0
2024-06-27T19:22:01.310Z
Various Git clients have options to ignore whitespace, so indentation changing isn't *that* much of a problem
Glorfindel
CC BY-SA 4.0
2024-06-29T08:49:03.680Z
Provided an answer as there seems not to be a consensus to close and cleanup the question. I'm still in favor of removing the whitespace part as it is meaningless syntax and splitting the throws / nothrow part into a separate question.
feldentm
<p>The lack of an explicit <code>try</code> makes it harder to catch exceptions from only some places.</p>
<p>Put together with <a href="https://langdev.stackexchange.com/q/3911/89">your other proposal</a> to make exceptions homogeneous, it will be harder in multiple ways for programmers to catch and handle some exceptional cases while allowing others to propagate. It will also be more likely that programmers will mistakenly catch (and accidentally suppress) exceptions they did not intend to.</p>
<hr />
<p>Consider code like this:</p>
<pre><code>file = acquire_foo()

try:
    foo.bar()
    foo.baz()
catch:
    print('failed to bar and baz the foo')
</code></pre>
<p>This shape of code is very common, where we want to acquire a resource and do something with it, and handle the error if the latter fails. Note that the <code>try:</code> specifically occurs after <code>acquire_foo()</code>, meaning if we fail to acquire the resource, the exception will not be caught. This is the programmer's intention, but expressing that intention requires an explicit <code>try</code>. There is no easy way to write this code otherwise.</p>
<p>You could get around this by making <code>catch</code> only apply to the preceding statement, and allowing grouping using optional braces:</p>
<pre><code>foo = acquire_foo()

{ // these together are one statement
    foo.bar()
    foo.baz()
}
catch:
    // this only catches exceptions from the previous block statement
    ...
</code></pre>
<p>However, besides having the same indentation you were trying to avoid by omitting <code>try</code>, this is an accident waiting to happen. People will forget to do this. It's particularly a problem when <code>acquire_foo()</code> and <code>foo.bar()</code> can signal the same type of exception (e.g. a database error), because if you catch from both by accident, you'll mishandle <code>acquire_foo()</code>'s error as if it was thrown by <code>foo.bar()</code>.</p>

CC BY-SA 4.0
2024-06-27T10:31:26.527Z
2024-06-27T10:31:26.527Z
kaya3
CC BY-SA 4.0
2024-06-27T11:48:33.027Z
Thanks a lot for your answer! I think the "resource closing" issue is best resolved in another way (always close if it goes out of scope, unless re-assigned; similar to a unique pointer). The "only catch some exception" can be resolved using an explicit scope by adding eg. "if true", if that should be needed. But I value very much your opinion!
Thomas Mueller
CC BY-SA 4.0
2024-06-27T12:08:47.957Z
@ThomasMueller The issue is not generally about closing the resource, but rather that you don't want to handle an exception thrown when opening the resource. For example, if the resource is a database, you often want to return a specific error from the current function if the current query fails, but if connecting to the database fails then you want the exception to bubble up. Closing is handled automatically in constructs like Java's `try`-with or Python's `with`, but note how in `try(Foo foo = acquireFoo()) { ... }`, an exception thrown by `acquireFoo()` isn't caught locally.
kaya3
CC BY-SA 4.0
2024-06-27T12:26:24.730Z
I see! For this case, there are two options: one is to have separate `catch` blocks for the "open" and "use" parts (in which case no indentation is needed at all), or then define a separate scope for the "use" part. But still, I think `try` is not needed (more harmful than helpful).
Thomas Mueller
CC BY-SA 4.0
2024-06-27T14:16:26.420Z
@ThomasMueller If it's possible to have more than one `catch` within a scope, then you have an equivalent of a `try` block, even if it's defined implicitly as "all lines from start-of-scope to `catch`, or from `catch` to next `catch`". Whether those blocks are indented is in most languages up to the user anyway, and they'll probably naturally indent anything that "feels like" a scope - for instance, the lines between `case` and `break` in a C-style switch statement aren't technically a block, but most people will indent them as though they were.
IMSoP
CC BY-SA 4.0
2024-06-27T16:01:15.030Z
@IMSoP right! I just want to avoid having to define an explicit scope, and so reduce the indentation / shrink the diff, where the programmer is fine with the existing scoping. C switch case: yes I know, [Duff's device](https://en.wikipedia.org/wiki/Duff%27s_device)
Thomas Mueller
CC BY-SA 4.0
2024-06-27T16:52:50.480Z
"People will forget to do this." -- similar to the problems that occur when people don't put the single-statement body of `if` in braces. https://stackoverflow.com/questions/359732/why-is-it-considered-a-bad-practice-to-omit-curly-braces?lq=1
Barmar
CC BY-SA 4.0
2024-06-27T17:03:35.707Z
@ThomasMueller I don't mean any fancy tricks, just plain, block-like switch statements: https://gist.github.com/IMSoP/43e9b164b0fde235ef1a43649f87dc75 I predict that if you have implied try blocks based on lexical order, people will write a comment like `// try`, and indent the rows covered by a particular `catch` statement.
IMSoP
CC BY-SA 4.0
2024-06-27T18:16:17.687Z
@IMSoP for "switch / case", yes it is obvious. For "catch", I think time will tell...
Thomas Mueller
CC BY-SA 4.0
2024-06-27T22:14:52.353Z
@kaya3: If a function is supposed to open a database connection, perform a query, and close a connection, having the function distinguish via some means a failure to open the connection versus a communications failure while trying to perform the query versus an affirmative rejection of the query would certainly be useful, but in many cases the essential facts are "You tried to do something.  It didn't work."  I suspect passing a failure callback may for many purposes be a better mechanism than an exception, since that would allow for the possibility of asking "Retry y/n?".
supercat
<p>The <code>try</code> in modern languages is just for the improvement of readability. If you look at it from a language processing perspective, the parse rules is usually something like this: <code>&lt;try&gt; block() &lt;catch&gt; handler()</code> Here, it does not really matter what <code>block</code> or <code>handler</code> rules really look like. Removing the <code>try</code> keyword would result in a transformation of the block rule users to become essentially <code>block() (&lt;catch&gt; handler())?</code>. And AST processing would have to check for the option and create the handler whenever a catch block is present.</p>
<p>Ada does it, example: <a href="https://learn.adacore.com/courses/intro-to-ada/chapters/exceptions.html#handling-an-exception" rel="nofollow noreferrer">https://learn.adacore.com/courses/intro-to-ada/chapters/exceptions.html#handling-an-exception</a></p>
<p>Ada is one of the oldest languages. So, we can assume that most language authors who were aware of it decided against it because they noticed that it reduces the readability of code if the target block is too large. If you look at code that doesn't fit onto the screen anymore and you lack the <code>try</code> in your language, you will be made aware of the catch only after reading a lot of code when reading it top down.</p>
<p>Go's <a href="https://go.dev/blog/defer-panic-and-recover" rel="nofollow noreferrer">defer/recover</a> approach is also not using a try. Evaluating it can only be done in the light of Go's view on exceptions that essentially is that exception handling should not be used at all. It is a good way of preventing programmers coming from other languages to continue using exception handling for handling exceptional situations.</p>
<p>Regarding the throws declaration that is part of the current form the question, I'd suggest splitting it to another question. Nonetheless, a similar concept exists in both C++ and Java. It should be discussed in a separate question because the answer interacts with other concepts in the language and the presence of implicit exceptions.</p>

CC BY-SA 4.0
2024-06-29T08:44:46.003Z
2024-06-29T08:44:46.003Z
feldentm
<p>Trying this out in practice with <a href="https://c3-lang.org" rel="nofollow noreferrer">C3</a> some years ago. I found that it was actually less clear than I expected – plus it didn't yield any additional useful semantics nor flexibility over regular try/catch handling.</p>
<p>Even if the <code>try</code> is explicitly stated in some way, like</p>
<pre class="lang-python prettyprint-override"><code>a := try foo()
doSomething(a)
catch e
  ...error_handling...
</code></pre>
<ul>
<li>it's still difficult to make out the error flow clearly.</li>
</ul>
<p>(In the end, C3 got a fairly unique blend of implicitly flatmapped Result/Optional, but that's a different story)</p>

CC BY-SA 4.0
2024-07-12T16:21:59.347Z
2024-07-12T16:21:59.347Z
Nuoji
<p>Most answers, and perhaps the OP as well, assume that there still is a well-defined scope on which the catch handler acts. However, there are languages which allow you to define exception handlers that work globally. Consider some variants of BASIC which have:</p>
<pre><code>ON ERROR GOSUB &lt;line&gt;
</code></pre>
<p>Note that this statement can install an exception handler for exceptions occuring outside of the scope of this statement itself.</p>
<p>The problem with this approach is that exception handling is not composable. So tying it to a scope, whether implicit or explicit with a <code>try</code> keyword, is preferrable.</p>
<p>An issue that arises when you don't have an explicit scope is how you define the implicit scope in an intuitive and robust way. What do you expect happens in the following case, when <code>foo()</code> does not throw but <code>bar()</code> does?</p>
<pre><code>foo()
catch e:
    handle_exception(e)
bar()
</code></pre>
<p>You could say that <code>catch</code> only catches exceptions from code that came before it. But then consider that not all code is a linear sequence:</p>
<pre><code>for i in range(10):
    foo()
    catch e:
        handle_exception(e)
    bar()
</code></pre>
<p>Does it catch exceptions from the second invocation of <code>bar()</code>? Without a <code>try</code>, <code>catch</code> looks like a regular statement. What if someone wants to make error handling depend on something else?</p>
<pre><code>foo()
if bar:
    catch e:
        handle_exception(e)
</code></pre>
<p>Now one must really know the rules to be able to tell if the <code>catch</code> will still apply to exceptions thrown by <code>foo()</code>, or if it's a no-op because its scope is only the body of the <code>if</code>-statement.</p>

CC BY-SA 4.0
2024-06-30T12:04:26.303Z
2024-06-30T12:04:26.303Z
G. Sliepen
CC BY-SA 4.0
2024-06-30T12:14:18.150Z
> "Does it catch exceptions from the second invocation of bar()?" I think that depends on the specification. I would argue that exceptions in bar() are not caught, unless the "catch" part is moved to be lower. My personal feeling is that "knowing the rules" is not terribly complicated in this area, and the complexity is similar to the question "what is the lifetime of a variable?". The answer is not the same, but the complexity is similar.
Thomas Mueller
CC BY-SA 4.0
2024-06-30T12:19:00.700Z
foo() if bar / catch. There is no "conditional catch" in other languages I'm aware of. But it is simple to say "catch e if bar handle_exception(e)".
Thomas Mueller
CC BY-SA 4.0
2024-06-30T12:48:55.783Z
I personally agree with all that, but that's because of intuition from using other programming languages. It might not be obvious to everyone, and when designing a new programming language, it's also not something you have to blindly copy. If you can make something better by not following the usual conventions, that would be very interesting :)
G. Sliepen
<p>If <code>try</code> is technically just a scope marker, it is functionally redundant when scopes are already clear. It is no problem to define a language in which <code>try</code> can be &quot;folded&quot; directly into a preceding scope marker.</p>
<p>For example, it is trivial to treat</p>
<pre class="lang-none prettyprint-override"><code>while ...
  ...
catch e
  ...
</code></pre>
<p>as syntactic sugar for</p>
<pre class="lang-none prettyprint-override"><code>while ...
  try
    ...
  catch e
    ...
</code></pre>
<p>which is similar to Ruby's semantics.</p>
<p>Relatedly, if your language is expression based you can have an <code>&lt;expr&gt; catch &lt;name&gt; &lt;expr(name)&gt;</code> construct/operator/... that trivially expands to a longer form. For example, <a href="https://peps.python.org/pep-0463/" rel="noreferrer">this even was proposed for Python and only rejected for non-technical reasons</a>.</p>
<hr />
<p>What you should ask yourself is if exception handling for an <em>entire, given</em> scope is actually a good thing. Consider that your tiny example is intended to <code>catch</code> an error from factorial, but actually it would handle two to three things.</p>
<p>To clarify, let me write that part the way I would do for my dayjob:</p>
<pre class="lang-python prettyprint-override"><code>try:
   result = factorial(i)
except e:
   print('Factorial of', i, 'resulted in', e.message)
   break
else:
   print('Factorial of', i, 'is', result)
   i += 1
</code></pre>
<p>Notice how the factorial handler really only deals with the factorial execution! It does not handle whether formatting strings works, whether printing works, or even whether incrementing works. These are all actions where an error is not expected by your handler, so if one does occur it should absolutely be loud and definitely not accidentally silenced.<br />
Precisely handling errors is something you absolutely do want to offer. And probably (since correct error handling is your goal) you don't just want to offer it, but also encourage it - or even discourage imprecise error handling!</p>
<p>By inviting people to be sloppy and re-use logical scopes as error scopes, you make error handling worse. So at the very least, you should offer <em>something</em> to add explicit scopes. This can be a generic scope mechanism, and in your example syntax it would map to a separate keyword:</p>
<pre class="lang-none prettyprint-override"><code>while ...
  .
  .
  scope
    .
  catch e
    ...
</code></pre>
<p>But at this point you are almost all the way to having <code>try</code>, especially if the <code>scope</code> has little or no other use. So basically you have to ask yourself whether you are willing to pay the price of having a <code>try</code> keyword or not - because most if not all of the other cost you should be prepared to pay anyway.</p>

CC BY-SA 4.0
2024-06-27T16:52:06.723Z
2024-06-27T16:52:06.723Z
MisterMiyagi
CC BY-SA 4.0
2024-06-27T18:21:31.153Z
Thanks for the Python link! I think leaving out the "try" is not "sloppy", but a conscious decision to _not_ have something. I understand removing things may not be popular for some people, that's fine. Btw. `if true` can be used instead of `scope`.
Thomas Mueller
CC BY-SA 4.0
2024-06-27T18:22:52.677Z
@ThomasMueller It’s not leaving out the `try` that is sloppy. It’s covering a far larger block than needed that is sloppy.
MisterMiyagi
CC BY-SA 4.0
2024-06-27T18:26:02.553Z
I don't see how this is sloppy in any way, I'm sorry. What is the harm? I found that "try" is the very first statement of a block in many Java projects (for Apache Jackrabbit, the previous line of a "try" contains "{" in almost half the cases). The "try" (if you have it) will also cover lines in-between, even if they do not throw exceptions. People don't have a problem with that usually.
Thomas Mueller
CC BY-SA 4.0
2024-06-27T18:44:02.093Z
@ThomasMueller The harm, as explained in the answer for example, is that the handler will unintentionally catch errors it wasn’t supposed to catch. Especially for suppressing handlers, this means that veritable bugs are silenced. As for people having a problem with too broad exception handling, consider that for example [the Python style guide](https://peps.python.org/pep-0008/) recommends both small `try` blocks and precise handlers exactly for the reason I mentioned here.
MisterMiyagi
CC BY-SA 4.0
2024-06-27T19:01:14.253Z
@ThomasMueller Sure you can recommend people use `if true` to force scopes - but this then really only saves you not reserving the `try`/`scope` *keyword*. Is that minor saving really worth having people write less intuitive code?
MisterMiyagi
CC BY-SA 4.0
2024-06-27T20:44:27.990Z
@ThomasMueller If you have a scope that’s fifty lines long, but only two of those lines are expected to possibly throw an exception outside of cases you truly can’t recover from (for example, memory errors), is it better to cover just those two lines that you expect to possibly throw an exception, or the whole block? Any sane programmer is going to prefer to cover just those two lines, because any other line in that block throwing an exception _should_ stop the program, because it’s either a bug or some other major issue.
Austin Hemmelgarn
CC BY-SA 4.0
2024-06-27T22:08:45.897Z
@AustinHemmelgarn: It would be helpful if languages provided an easy means of marking portions of a function that were and were not expected to throw exceptions, and be able to have a unified catch block for any exceptions which occurred in any of the expected places, rather than having to have a separate catch block just before every part of the code that wasn't expected to throw.
supercat
CC BY-SA 4.0
2024-06-28T01:26:49.503Z
@supercat I don’t entirely agree. If all the code that might throw an exception is not right next to each other, it’s very likely that you want at least some differences in handling of the exceptions, and clearly associating those differences with the parts of the code they apply to can be _very_ important for ease of understanding.
Austin Hemmelgarn
CC BY-SA 4.0
2024-06-28T14:57:18.027Z
@AustinHemmelgarn: If e.g. a function is processing data from an outside client that is supposed to have pre-validated it, and parts of the function that are recognized as being able to throw when given invalid data in fact throw, the response should be to notify the client that the operation failed.  Often, however, parts that might be expected to throw when given certain invalid data will be interleaved with operations where throwing wouldn't even be thought of as a possibility.  Operations on received data might be interleaved, for example, with operations on array indices.
supercat
CC BY-SA 4.0
2024-06-28T15:01:20.053Z
@AustinHemmelgarn: Today, programs would use a catch block that embodies both kinds of operations and simply lump any exceptions that occur in the unexpected spots together with expected exceptions.  Perhaps instead of just having an expected/unexpected distinction, it should be possible to define function-local categories of exceptions which would need to be caught within the function that defines them, and would need to be wrapped before being re-thrown.
supercat
CC BY-SA 4.0
2024-07-02T14:35:40.733Z
It's not quite as simple as you make out. With the `while` loop, for example, there is the choice of whether `catch` applies to an exception raised by the loop condition as well, or just the loop body. (It's also not entirely clear that `catch` only applies to a single preceding statement or everything previous the current block, thought that's a fine default if the language allows for arbitrary compound statements.)
chepner
CC BY-SA 4.0
2024-07-02T17:12:01.723Z
@chepner I'm just saying it is trivial to make well-defined, not that I would personally recommend it as being obvious. There are lots of cases where trailing `try` would be non-obvious, e.g. trailing any `if`, `elif`, ..., `else` multi-block. The `while` case also gets interesting if `catch` has any loop control such as `continue` - would that affect the trailing `while` or an outer loop? However, IMO that isn't terribly relevant since a trailing/scope `catch` is a nuclear option anyway; it won't be suitable for precision no matter which "option" is chosen in ambiguous cases.
MisterMiyagi
# Exception handling: 'catch' without explicit 'try'
Posted by: username_0 on 2024-06-27
(I was not aware of this site and asked, by mistake, a similar question on cs.stackexchange as well. It is related to this question on this site.)
Context
I'm working on a new general-purpose programming language. Exception handling is supposed to be like in Rust, but simpler. I'm specially interested in feedback for exception handling (throw, catch).
I think, same as in Swift and Rust (and Go), exception should be handled immediately, and if not, either the program must stop, or the function must throw an exception. So, no undeclared exceptions. This is what "catch is needed, or the method needs throws" means.
Question

The catch catches all exceptions that were thrown within the scope. I argue there is no need for try, because try would requires (unnecessary, in my view) indentation, and messes up diffs. In Ruby, the equivalent of try is optional as well. Are there other languages like that? I didn't find any. Is there a problem with this idea?

Source Code Example
throw throws an exception. catch is needed, or the method needs throws:
fun factorial(x int) int throws
    if x > 20
        throw exception('Value too large')
    if x <= 1
        return 1
    f := factorial(x - 1)
  return x * f

fun main()
    i := 0
    while i <= 30
        println('Factorial of ' i ' is ' factorial(i))
        catch e
            println('Factorial of ' i ' resulted in ' e.message)
        i += 1
**Tags:** error-handling exceptions
> **Comments:**
> **username_1**: Rust has [a unsable feature for a try block](https://doc.rust-lang.org/beta/unstable-book/language-features/try-blocks.html)  
> **username_2**: You might find [this article about the error handling from the Midori project](https://joeduffyblog.com/2016/02/07/the-error-model/) interesting. For instance, it allowed a definition of `int Foo() throws { ... }`, and a call-site sugar of `Result<int> value = try Foo() else catch;`  
> **username_3**: What *is* a scope in your language? A function? A statement? Something else? What happens when `catch` is in the middle of a block - does its scope span the entire block, including after it? Does your language have the means to explicitly create a new scope?  
> **username_0**: I have now updated the code example. A scope is a function, or block (e.g. `if` or `while` block). If there is a `catch` in the middle, I didn't think about that, but I assume technically it is still the same scope, the same as if there is an `if` statement in the middle of a block. You can create a new (nested) scope by using, for example `if true`.  
> **username_4**: The `if true` option means you get *two* levels of indentation where `try`/`catch` would have one, since the `catch` block must be indented one level further than the `if` block. Besides, idioms (like writing `if true` where the programmer's intention is `try`) generally arise from users working around flaws or oversights in the language's design, so it doesn't bode well if an idiom like that is the "officially recommended" way of doing something.  
> **username_0**: @kaya3 there is no `try`, so no, there is only one level. I don't see the reason to use `if true` in practise... you extend the scope slightly, I don't see much harm. I also don't see much harm in making the uncommon cases slightly harder, if this simplifies the common case.  
> **username_5**: It is obvious that a catch can be attached to arbitrary blocks, so the question would not be jutsified. I vote to close because in its current form the question is more of an answer with a lot of opinion. Also, the logic is inconsistent. And the question contains unrelated details.  
> **username_4**: @ThomasMueller As I said, you have the indentation for the `if` block, and then the `catch` block is indented further than that. If you had `try` instead of `if` then the `catch` block would be at the same level of indentation, instead of nested. As for which is the "common" case, your way is only correct if a `try` statement would be the first statement in its block, which in my experience is not that common.  
> **username_0**: @kaya3 yes the catch is indented, I understand now what you mean. As for "is try the first statement in the block?", I tested this and found (in Apache Jackrabbit) that there are 2566 "try" blocks, and in 1053 the previous line contains "{"; in 189 cases it contains "}". Meaning that in many cases, "try" is indeed the first statement in a block. For this project. I didn't investigate much further (there are many empty lines, and I didn't check if other lines might or might not throw other exceptions).  
> **username_6**: Various Git clients have options to ignore whitespace, so indentation changing isn't *that* much of a problem  
> **username_5**: Provided an answer as there seems not to be a consensus to close and cleanup the question. I'm still in favor of removing the whitespace part as it is meaningless syntax and splitting the throws / nothrow part into a separate question.  
---
## 5 answers
### Answer by username_3 on 2024-06-27. Score: 7 | Accepted answer
If try is technically just a scope marker, it is functionally redundant when scopes are already clear. It is no problem to define a language in which try can be "folded" directly into a preceding scope marker.
For example, it is trivial to treat
while ...
  ...
catch e
  ...

as syntactic sugar for
while ...
  try
    ...
  catch e
    ...

which is similar to Ruby's semantics.
Relatedly, if your language is expression based you can have an  catch   construct/operator/... that trivially expands to a longer form. For example, this even was proposed for Python and only rejected for non-technical reasons.

What you should ask yourself is if exception handling for an entire, given scope is actually a good thing. Consider that your tiny example is intended to catch an error from factorial, but actually it would handle two to three things.
To clarify, let me write that part the way I would do for my dayjob:
try:
   result = factorial(i)
except e:
   print('Factorial of', i, 'resulted in', e.message)
   break
else:
   print('Factorial of', i, 'is', result)
   i += 1

Notice how the factorial handler really only deals with the factorial execution! It does not handle whether formatting strings works, whether printing works, or even whether incrementing works. These are all actions where an error is not expected by your handler, so if one does occur it should absolutely be loud and definitely not accidentally silenced.
Precisely handling errors is something you absolutely do want to offer. And probably (since correct error handling is your goal) you don't just want to offer it, but also encourage it - or even discourage imprecise error handling!
By inviting people to be sloppy and re-use logical scopes as error scopes, you make error handling worse. So at the very least, you should offer something to add explicit scopes. This can be a generic scope mechanism, and in your example syntax it would map to a separate keyword:
while ...
  .
  .
  scope
    .
  catch e
    ...

But at this point you are almost all the way to having try, especially if the scope has little or no other use. So basically you have to ask yourself whether you are willing to pay the price of having a try keyword or not - because most if not all of the other cost you should be prepared to pay anyway.
> **Comments:**
> **username_0** (2024-06-27): Thanks for the Python link! I think leaving out the "try" is not "sloppy", but a conscious decision to _not_ have something. I understand removing things may not be popular for some people, that's fine. Btw. `if true` can be used instead of `scope`.  
> **username_3** (2024-06-27): @ThomasMueller It’s not leaving out the `try` that is sloppy. It’s covering a far larger block than needed that is sloppy.  
> **username_0** (2024-06-27): I don't see how this is sloppy in any way, I'm sorry. What is the harm? I found that "try" is the very first statement of a block in many Java projects (for Apache Jackrabbit, the previous line of a "try" contains "{" in almost half the cases). The "try" (if you have it) will also cover lines in-between, even if they do not throw exceptions. People don't have a problem with that usually.  
> **username_3** (2024-06-27): @ThomasMueller The harm, as explained in the answer for example, is that the handler will unintentionally catch errors it wasn’t supposed to catch. Especially for suppressing handlers, this means that veritable bugs are silenced. As for people having a problem with too broad exception handling, consider that for example [the Python style guide](https://peps.python.org/pep-0008/) recommends both small `try` blocks and precise handlers exactly for the reason I mentioned here.  
> **username_3** (2024-06-27): @ThomasMueller Sure you can recommend people use `if true` to force scopes - but this then really only saves you not reserving the `try`/`scope` *keyword*. Is that minor saving really worth having people write less intuitive code?  
> **username_11** (2024-06-27): @ThomasMueller If you have a scope that’s fifty lines long, but only two of those lines are expected to possibly throw an exception outside of cases you truly can’t recover from (for example, memory errors), is it better to cover just those two lines that you expect to possibly throw an exception, or the whole block? Any sane programmer is going to prefer to cover just those two lines, because any other line in that block throwing an exception _should_ stop the program, because it’s either a bug or some other major issue.  
> **username_8** (2024-06-27): @AustinHemmelgarn: It would be helpful if languages provided an easy means of marking portions of a function that were and were not expected to throw exceptions, and be able to have a unified catch block for any exceptions which occurred in any of the expected places, rather than having to have a separate catch block just before every part of the code that wasn't expected to throw.  
> **username_11** (2024-06-28): @supercat I don’t entirely agree. If all the code that might throw an exception is not right next to each other, it’s very likely that you want at least some differences in handling of the exceptions, and clearly associating those differences with the parts of the code they apply to can be _very_ important for ease of understanding.  
> **username_8** (2024-06-28): @AustinHemmelgarn: If e.g. a function is processing data from an outside client that is supposed to have pre-validated it, and parts of the function that are recognized as being able to throw when given invalid data in fact throw, the response should be to notify the client that the operation failed.  Often, however, parts that might be expected to throw when given certain invalid data will be interleaved with operations where throwing wouldn't even be thought of as a possibility.  Operations on received data might be interleaved, for example, with operations on array indices.  
> **username_8** (2024-06-28): @AustinHemmelgarn: Today, programs would use a catch block that embodies both kinds of operations and simply lump any exceptions that occur in the unexpected spots together with expected exceptions.  Perhaps instead of just having an expected/unexpected distinction, it should be possible to define function-local categories of exceptions which would need to be caught within the function that defines them, and would need to be wrapped before being re-thrown.  
> **username_12** (2024-07-02): It's not quite as simple as you make out. With the `while` loop, for example, there is the choice of whether `catch` applies to an exception raised by the loop condition as well, or just the loop body. (It's also not entirely clear that `catch` only applies to a single preceding statement or everything previous the current block, thought that's a fine default if the language allows for arbitrary compound statements.)  
> **username_3** (2024-07-02): @chepner I'm just saying it is trivial to make well-defined, not that I would personally recommend it as being obvious. There are lots of cases where trailing `try` would be non-obvious, e.g. trailing any `if`, `elif`, ..., `else` multi-block. The `while` case also gets interesting if `catch` has any loop control such as `continue` - would that affect the trailing `while` or an outer loop? However, IMO that isn't terribly relevant since a trailing/scope `catch` is a nuclear option anyway; it won't be suitable for precision no matter which "option" is chosen in ambiguous cases.  
---
### Answer by username_4 on 2024-06-27. Score: 5
The lack of an explicit try makes it harder to catch exceptions from only some places.
Put together with your other proposal to make exceptions homogeneous, it will be harder in multiple ways for programmers to catch and handle some exceptional cases while allowing others to propagate. It will also be more likely that programmers will mistakenly catch (and accidentally suppress) exceptions they did not intend to.

Consider code like this:
file = acquire_foo()

try:
    foo.bar()
    foo.baz()
catch:
    print('failed to bar and baz the foo')

This shape of code is very common, where we want to acquire a resource and do something with it, and handle the error if the latter fails. Note that the try: specifically occurs after acquire_foo(), meaning if we fail to acquire the resource, the exception will not be caught. This is the programmer's intention, but expressing that intention requires an explicit try. There is no easy way to write this code otherwise.
You could get around this by making catch only apply to the preceding statement, and allowing grouping using optional braces:
foo = acquire_foo()

{ // these together are one statement
    foo.bar()
    foo.baz()
}
catch:
    // this only catches exceptions from the previous block statement
    ...

However, besides having the same indentation you were trying to avoid by omitting try, this is an accident waiting to happen. People will forget to do this. It's particularly a problem when acquire_foo() and foo.bar() can signal the same type of exception (e.g. a database error), because if you catch from both by accident, you'll mishandle acquire_foo()'s error as if it was thrown by foo.bar().
> **Comments:**
> **username_0** (2024-06-27): Thanks a lot for your answer! I think the "resource closing" issue is best resolved in another way (always close if it goes out of scope, unless re-assigned; similar to a unique pointer). The "only catch some exception" can be resolved using an explicit scope by adding eg. "if true", if that should be needed. But I value very much your opinion!  
> **username_4** (2024-06-27): @ThomasMueller The issue is not generally about closing the resource, but rather that you don't want to handle an exception thrown when opening the resource. For example, if the resource is a database, you often want to return a specific error from the current function if the current query fails, but if connecting to the database fails then you want the exception to bubble up. Closing is handled automatically in constructs like Java's `try`-with or Python's `with`, but note how in `try(Foo foo = acquireFoo()) { ... }`, an exception thrown by `acquireFoo()` isn't caught locally.  
> **username_0** (2024-06-27): I see! For this case, there are two options: one is to have separate `catch` blocks for the "open" and "use" parts (in which case no indentation is needed at all), or then define a separate scope for the "use" part. But still, I think `try` is not needed (more harmful than helpful).  
> **username_2** (2024-06-27): @ThomasMueller If it's possible to have more than one `catch` within a scope, then you have an equivalent of a `try` block, even if it's defined implicitly as "all lines from start-of-scope to `catch`, or from `catch` to next `catch`". Whether those blocks are indented is in most languages up to the user anyway, and they'll probably naturally indent anything that "feels like" a scope - for instance, the lines between `case` and `break` in a C-style switch statement aren't technically a block, but most people will indent them as though they were.  
> **username_0** (2024-06-27): @IMSoP right! I just want to avoid having to define an explicit scope, and so reduce the indentation / shrink the diff, where the programmer is fine with the existing scoping. C switch case: yes I know, [Duff's device](https://en.wikipedia.org/wiki/Duff%27s_device)  
> **username_7** (2024-06-27): "People will forget to do this." -- similar to the problems that occur when people don't put the single-statement body of `if` in braces. https://stackoverflow.com/questions/359732/why-is-it-considered-a-bad-practice-to-omit-curly-braces?lq=1  
> **username_2** (2024-06-27): @ThomasMueller I don't mean any fancy tricks, just plain, block-like switch statements: https://gist.github.com/IMSoP/43e9b164b0fde235ef1a43649f87dc75 I predict that if you have implied try blocks based on lexical order, people will write a comment like `// try`, and indent the rows covered by a particular `catch` statement.  
> **username_0** (2024-06-27): @IMSoP for "switch / case", yes it is obvious. For "catch", I think time will tell...  
> **username_8** (2024-06-27): @kaya3: If a function is supposed to open a database connection, perform a query, and close a connection, having the function distinguish via some means a failure to open the connection versus a communications failure while trying to perform the query versus an affirmative rejection of the query would certainly be useful, but in many cases the essential facts are "You tried to do something.  It didn't work."  I suspect passing a failure callback may for many purposes be a better mechanism than an exception, since that would allow for the possibility of asking "Retry y/n?".  
---
### Answer by username_5 on 2024-06-29. Score: 2
The `try` in modern languages is just for the improvement of readability. If you look at it from a language processing perspective, the parse rules is usually something like this:  `block()  handler()` Here, it does not really matter what `block` or `handler` rules really look like. Removing the `try` keyword would result in a transformation of the block rule users to become essentially `block() ( handler())?`. And AST processing would have to check for the option and create the handler whenever a catch block is present.

Ada does it, example: <https://learn.adacore.com/courses/intro-to-ada/chapters/exceptions.html#handling-an-exception>

Ada is one of the oldest languages. So, we can assume that most language authors who were aware of it decided against it because they noticed that it reduces the readability of code if the target block is too large. If you look at code that doesn't fit onto the screen anymore and you lack the `try` in your language, you will be made aware of the catch only after reading a lot of code when reading it top down.

Go's [defer/recover](https://go.dev/blog/defer-panic-and-recover) approach is also not using a try. Evaluating it can only be done in the light of Go's view on exceptions that essentially is that exception handling should not be used at all. It is a good way of preventing programmers coming from other languages to continue using exception handling for handling exceptional situations.

Regarding the throws declaration that is part of the current form the question, I'd suggest splitting it to another question. Nonetheless, a similar concept exists in both C++ and Java. It should be discussed in a separate question because the answer interacts with other concepts in the language and the presence of implicit exceptions.
---
### Answer by username_9 on 2024-07-12. Score: 1
Trying this out in practice with C3 some years ago. I found that it was actually less clear than I expected – plus it didn't yield any additional useful semantics nor flexibility over regular try/catch handling.
Even if the try is explicitly stated in some way, like
a := try foo()
doSomething(a)
catch e
  ...error_handling...


it's still difficult to make out the error flow clearly.

(In the end, C3 got a fairly unique blend of implicitly flatmapped Result/Optional, but that's a different story)
---
### Answer by username_10 on 2024-06-30. Score: 1
Most answers, and perhaps the OP as well, assume that there still is a well-defined scope on which the catch handler acts. However, there are languages which allow you to define exception handlers that work globally. Consider some variants of BASIC which have:
ON ERROR GOSUB 

Note that this statement can install an exception handler for exceptions occuring outside of the scope of this statement itself.
The problem with this approach is that exception handling is not composable. So tying it to a scope, whether implicit or explicit with a try keyword, is preferrable.
An issue that arises when you don't have an explicit scope is how you define the implicit scope in an intuitive and robust way. What do you expect happens in the following case, when foo() does not throw but bar() does?
foo()
catch e:
    handle_exception(e)
bar()

You could say that catch only catches exceptions from code that came before it. But then consider that not all code is a linear sequence:
for i in range(10):
    foo()
    catch e:
        handle_exception(e)
    bar()

Does it catch exceptions from the second invocation of bar()? Without a try, catch looks like a regular statement. What if someone wants to make error handling depend on something else?
foo()
if bar:
    catch e:
        handle_exception(e)

Now one must really know the rules to be able to tell if the catch will still apply to exceptions thrown by foo(), or if it's a no-op because its scope is only the body of the if-statement.
> **Comments:**
> **username_0** (2024-06-30): > "Does it catch exceptions from the second invocation of bar()?" I think that depends on the specification. I would argue that exceptions in bar() are not caught, unless the "catch" part is moved to be lower. My personal feeling is that "knowing the rules" is not terribly complicated in this area, and the complexity is similar to the question "what is the lifetime of a variable?". The answer is not the same, but the complexity is similar.  
> **username_0** (2024-06-30): foo() if bar / catch. There is no "conditional catch" in other languages I'm aware of. But it is simple to say "catch e if bar handle_exception(e)".  
> **username_10** (2024-06-30): I personally agree with all that, but that's because of intuition from using other programming languages. It might not be obvious to everyone, and when designing a new programming language, it's also not something you have to blindly copy. If you can make something better by not following the usual conventions, that would be very interesting :)  
---
