Error Handling
Try/catch/finally for exception handling, throw for raising errors.
Try / Catch / Finally
try / catch / finally
Handles exceptions with try/catch blocks, using end-delimited syntax just like if, while, and for. Ruby-style aliases are supported: begin for try, and rescue for catch.
# Basic try/catch
try
result = 10 / 0
catch e
print("Error: " + str(e))
end
# With finally (always runs)
try
data = read_file("config.sl")
print(data)
catch e
print("Failed: " + str(e))
finally
print("Cleanup done")
end
# Try/finally without catch
try
process_data()
finally
close_connection()
end
# Ruby-style aliases: `begin` for `try`, `rescue` for `catch`
begin
risky_operation()
rescue e
print("Error: " + str(e))
end
Catch Variable Syntax
The catch variable can be written with or without parentheses:
# Without parentheses
try
risky()
catch e
print(e)
end
# With parentheses
try
risky()
catch (e)
print(e)
end
Throwing Exceptions
throw
Raises an exception that can be caught by a surrounding try/catch block.
def divide(a: Int, b: Int) -> Int
if b == 0
throw "Division by zero"
end
a / b
end
try
result = divide(10, 0)
catch e
print("Caught: " + str(e)) # "Caught: Division by zero"
end
End Syntax
Try/catch supports end-delimited blocks:
try
result = risky_operation()
catch e
print("Error: " + str(e))
finally
cleanup()
end
Typed Catch
catch ClassName e
Catch specific error types by class name. Multiple catch blocks are tried in order. A bare catch without a type acts as a catch-all.
# Define error classes
class NotFoundError
message: String
new(msg: String)
this.message = msg
end
end
class ValidationError
message: String
new(msg: String)
this.message = msg
end
end
# Multiple typed catch blocks
try
throw new NotFoundError("User not found")
catch NotFoundError e
print("404: " + e.message)
catch ValidationError e
print("Invalid: " + e.message)
catch e
print("Unknown error: " + str(e))
end
Catching by Superclass
Typed catches walk the inheritance chain. A catch BaseError will also catch any subclass of BaseError.
class AppError
message: String
new(msg: String)
this.message = msg
end
end
class NotFoundError < AppError
new(msg: String)
super(msg)
end
end
class PermissionError < AppError
new(msg: String)
super(msg)
end
end
# Catches NotFoundError because it extends AppError
try
throw new NotFoundError("Page not found")
catch AppError e
print("App error: " + e.message)
end
Unmatched Exceptions
If no typed catch matches, the exception is re-thrown to the next outer try/catch. Add a bare catch at the end to handle all remaining exceptions.
class ErrorA {}
class ErrorB {}
try
try
throw new ErrorB()
catch ErrorA e
# Does NOT match ErrorB
print("A")
end
catch e
# ErrorB bubbles up here
print("Caught in outer: " + str(e))
end
Typed Catch Rules
- Typed catches only match class instances (strings, ints, etc. will not match)
- Catches are tried in order — put more specific types first
- A bare
catch ecatches everything (including non-instance values) - Subclasses match their parent class catches (walks the inheritance chain)
- Works with both
endand{}syntax
Nested Try/Catch
Try/catch blocks can be nested for fine-grained error handling:
try
print("Outer try")
try
throw "inner error"
catch e
print("Inner catch: " + str(e))
end
print("After inner try")
catch e
print("Outer catch: " + str(e))
end
Postfix Rescue
expr rescue fallback
Returns fallback if expr throws an exception. A concise alternative to try/catch for simple cases.
# Simple rescue
data = fetch_data() rescue null;
print(data); # null if fetch_data threw
# With fallback value
config = load_config() rescue {"host": "localhost", "port": 8080};
# Chaining with nullish coalescing
result = risky_operation() rescue null ?? "default";
# In pipelines
data = user_id |> fetch_user |> validate_user rescue null;
Precedence
Rescue has the same precedence as assignment, so x = y rescue z parses as x = (y rescue z).
# These are equivalent
x = risky() rescue "default";
x = (risky() rescue "default");
# Can chain with other operators
a = throw "err" rescue "x" or "y"; # ("x" or "y") is the fallback
When to Use Postfix Rescue
- Use for simple fallbacks where you just need a default value
- Use
try/catchwhen you need to handle the error, run cleanup code, or make decisions based on the error type - Rescue swallows the error - if you need to log or re-throw, use try/catch instead