Exception Handling

Quartz provides a robust exception handling system with Java-like syntax. Exceptions allow you to handle errors gracefully without crashing your program.

Basic Syntax

Try-Catch

Use try and catch to handle exceptions:

import system.io as io;

try {
// Code that might throw an exception
throw new Exception("Something went wrong!");
} catch (Exception e) {
// Handle the exception
io.out.println("Caught:", e.message);
}

Try-Catch-Finally

Use finally for cleanup code that always runs:

try {
// Risky operation
throw new RuntimeError("Operation failed");
} catch (RuntimeError e) {
io.out.println("Error:", e.message);
} finally {
// Always executed, even if exception occurred
io.out.println("Cleanup complete");
}

Throwing Exceptions

Throw a New Exception

throw new Exception("Error message");
throw new ValueError("Invalid input");
throw new RuntimeError("Operation failed");
throw new TypeError("Type mismatch");
throw new IndexError("Index out of bounds");

Throw a String

You can throw a string directly, which creates an Exception automatically:

throw "Simple error message";
// Equivalent to: throw new Exception("Simple error message");

Rethrow an Exception

try {
// ...
} catch (Exception e) {
// Log and rethrow
io.out.println("Logging error:", e.message);
throw e;
}

Built-in Exception Types

These exception types are available globally without importing:

Exception Type Use Case
`Exception` Base exception type, catches all exceptions
`RuntimeError` General runtime errors
`ValueError` Invalid value or argument
`TypeError` Type mismatch errors
`IndexError` Index out of bounds
`NullError` Null/undefined reference

Exception Properties

When you catch an exception, you can access these properties:

try {
throw new ValueError("Invalid input");
} catch (ValueError e) {
io.out.println("Type:", e.type);      // "ValueError"
io.out.println("Message:", e.message); // "Invalid input"
}

Catch Patterns

Catch Specific Type

try {
throw new IndexError("Out of bounds");
} catch (IndexError e) {
// Only catches IndexError
io.out.println("Index error:", e.message);
}

Catch Multiple Types

try {
// Some risky operation
} catch (ValueError e) {
io.out.println("Value error:", e.message);
} catch (TypeError e) {
io.out.println("Type error:", e.message);
} catch (Exception e) {
// Catch any other exception
io.out.println("Other error:", e.message);
}

Catch All Exceptions

try {
throw new RuntimeError("Something bad");
} catch (Exception e) {
// Exception catches all exception types
io.out.println("Caught:", e.type, "-", e.message);
}

The Finally Block

The finally block always executes, regardless of whether an exception was thrown or caught:

fn processFile() {
let file = openFile("data.txt");

try {
// Process file - might throw
processData(file);
} catch (Exception e) {
io.out.println("Error processing:", e.message);
} finally {
// Always close the file
closeFile(file);
io.out.println("File closed");
}
}

Best Practices

Be Specific

Catch specific exception types when possible:

// Good - specific handling
try {
parseNumber(input);
} catch (ValueError e) {
io.out.println("Invalid number format");
}

// Less good - catches everything
try {
parseNumber(input);
} catch (Exception e) {
io.out.println("Something went wrong");
}

Don't Swallow Exceptions

// Bad - exception is silently ignored
try {
riskyOperation();
} catch (Exception e) {
// Nothing here!
}

// Good - at least log it
try {
riskyOperation();
} catch (Exception e) {
io.out.println("Error:", e.message);
}

Use Finally for Cleanup

let resource = acquireResource();
try {
useResource(resource);
} finally {
releaseResource(resource);  // Always runs
}

Fail Fast

Validate inputs early and throw meaningful exceptions:

fn divide(a: double, b: double) -> double {
if (b == 0) {
throw new ValueError("Division by zero");
}
return a / b;
}

Complete Example

import system.io as io;
import system.convert as conv;

fn parseAge(input: string) -> int {
let age = conv.toInt(input);

if (age < 0) {
throw new ValueError("Age cannot be negative");
}
if (age > 150) {
throw new ValueError("Age seems unrealistic");
}

return age;
}

fn main() {
io.out.println("Enter your age:");
let input = io.stdin.readln();

try {
let age = parseAge(input);

if (age < 18) {
io.out.println("You are a minor");
} else if (age < 65) {
io.out.println("You are an adult");
} else {
io.out.println("You are a senior");
}
} catch (ValueError e) {
io.out.println("Invalid age:", e.message);
} catch (Exception e) {
io.out.println("Unexpected error:", e.message);
} finally {
io.out.println("Thank you for using the program!");
}
}

main();

Exception Hierarchy

Exception (base)
├── RuntimeError
├── ValueError
├── TypeError
├── IndexError
└── NullError

Catching Exception will catch all exception types. More specific types only catch their own exceptions.