Try expressions¶
The Result[T, E] type can be implicitly created via
try expressions. A try expression wraps the value of
the expression in Result[T, E] where E, if not
specified, is almost always the base Exception type, and T is
the type of the expression. If the expression raises an
exception, then the result holds its Err case.
x = try int("some string") # x is of type Result[int, Exception]
# With explicit type annotation:
x: Result[int, Exception] = try int("some string")
The only place where E is not automatically made to be the
base Exception type is in type casting of the form to where
it is InvalidCastException:
x = try my_dog to Cat # x is of type Result[Cat, InvalidCastException]
# With explicit type annotation:
x: Result[Cat, InvalidCastException] = try my_dog to Cat
A try expression can be specified for a specific type
where if the expression throws that type, then it is caught
inside Err case. Other types become uncaught exceptions
that must be handled by other means, e.g. try/except/finally.
x = try[ValueError] int("some string") # x is of type Result[int, ValueError]
# With explicit type annotation:
x: Result[int, ValueError] = try[ValueError] int("some string")
Uncaught exception types: When using try[E] with a specific exception type, only exceptions of type E (or its subclasses) are caught. If the expression throws a different exception type, that exception propagates uncaught at runtime:
# Only catches ValueError; TypeError propagates uncaught
x = try[ValueError] some_func()
# If some_func() raises TypeError, it is NOT caught by the try expression
# and propagates to the nearest enclosing try/except or terminates the program
It is not an error if the expression would never raise an
exception. In such cases, the result type is always Result[T, Exception] where T is the expression's type.
Precedence Rules:
The try expression has very low precedence (lower than to, arithmetic, comparisons, and logical operators), meaning it captures the entire following expression:
# try captures the full expression including arithmetic
x = try some_func(4) + 5 # Parsed as: try (some_func(4) + 5)
# Result[int, Exception] wrapping the sum
# try captures through comparisons and logical operators
y = try parse_int(s) > 0 and validate(s) # Parsed as: try (parse_int(s) > 0 and validate(s))
# Result[bool, Exception]
# try is lower precedence than `to`, so it wraps casts
z = try animal to Dog # Parsed as: try (animal to Dog)
# Result[Dog, InvalidCastException]
# try does NOT capture conditional expressions (which have lower precedence)
y = try foo() if cond else bar() # Parsed as: (try foo()) if cond else bar()
# try only applies to foo(), not bar()
# Parentheses make intent clear
y = try (foo() if cond else bar()) # try applies to entire conditional
Use parentheses when you need to limit what try captures:
Implementation
- 🔄 Lowered - try/catch pattern wrapping the expression.