In Python the try … except statement has an optional else clause. It is useful for code that should only be executed if the try clause does not raise an exception.
In Nim (like in C++) we have to write:
try:
# Line which might raise an exception.
# Potentially lots of
# other lines which
# should only be
# executed if no
# exception was raised.
except:
discard
with else this would read:
try:
# Line which might raise an exception.
except:
discard
else:
# Potentially lots of
# other lines which
# should only be
# executed if no
# exception was raised.
The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn't raised by the code being protected by the try … except statement.
IMO it also makes it clearer in what code an exception is expected to happen.
Do you think an optional else clause would be worth adding to Nim?
Nim has try finally that can be used for this, and in my view it makes more sense than an else clause on a try.
import std/strutils
proc parseAnInt(s: string) =
try:
discard parseInt(s)
finally:
if getCurrentException() == nil:
echo "We did parse"
parseAnInt"10"
parseAnInt"hello"
The workaround isn't all that bad:
var didRaise = false
try:
a
b
c
except:
didRaise = true
if not didRaise:
discard "Python's else part here"
finally and else are quite different though. finally will run in both cases, else will only run if nothing was actually raised. I agree that else in this case is a useful construct. The try block has its own scope, so it's a bit annoying to have to put lots of code inside a try block when it's only one thing which can throw an exception just because it must be scoped the same. Of course Nim has implicit returns for any block, so one can do something like:
let myInt =
try: parseInt(s)
except: echo "Error" # Quit, return, or raise here is fine. Otherwise we need to return a sentinel value we can check against later
One can also wrap these things in options and unpack those (e.g. with my optionsutils library), but I still think that else would be nice to have.
Since Python has scope leaking, this works:
try:
a = 5
except:
pass
else:
print(a)
finally:
print(a)
print(a)
Problem is that now the else-part is also within the try block. It's easy to end up in a scenario where you want to catch the exception from e.g. parseInt but then your following logic can also throw an exception you don't want to catch. You might not even be aware that it is capable of throwing an error, or the API could change between versions without you being aware. My point is that putting it in that else block would make sure you're only actually catching exceptions from the thing you want to. Of course you could write it in Nim as:
if (
try:
discard parseInt("123")
true
except:
false):
echo "Python's else part here"
Which is a bit meh, or even as:
block tryBlock:
try:
discard parseInt("123")
except:
break tryBlock
echo "Python's else part here"
Which is vaguely better, but the else part is still in a different scope.
e.g
fileExists(file):
try:
whole = readFile file
except IOError as e:
echo "\nCannot read '",file,"': ",e.msg
except:
echo "\nCannot read '",file,"': unidentified error"
else: echo "none"
use like so
block:
try:
doSomething() #might raise an error
except:
break
doSomethingElse() #Execute only when there's no error