Skip to content
Draft

Errors

Draft uses explicit error handling with Result and Option types. No exceptions.

Result<T, E> represents either success (ok) or failure (err):

enum Result<T, E>
ok(value: T)
err(error: E)
fin enum.
action divide(@a: Float, @b: Float) -> Result<Float, String>
if @b == 0.0
return err("Division by zero")
fin if.
return ok(@a / @b)
fin action.
@result = divide(10.0, 2.0)
match @result
when ok then write("Result: " + string(@result#value))
when err then write("Error: " + @result#error)
fin match.

Get the value or panic:

@value = divide(10.0, 2.0)#unwrap()
@value = divide(10.0, 0.0)#unwrap_or(0.0)

Use ? to propagate errors:

action process() -> Result<Void, Error>
@data = read_file("data.json")? @// Returns early on error
@parsed = parse(@data)?
save(@parsed)?
return ok(nothing)
fin action.
record Error
message: String
code: Int
fin record.
action fail() -> Result<Void, Error>
return err(Error { message: "Something went wrong", code: 500 })
fin action.

Option<T> represents a value that may or may not exist:

enum Option<T>
some(value: T)
none
fin enum.
action find_user(@id: Int) -> Option<User>
if @id < 0
return none
fin if.
return some(User { id: @id, name: "Alice" })
fin action.
@user = find_user(1)
match @user
when some then write(@user#value#name)
when none then write("User not found")
fin match.

For unrecoverable errors:

action critical()
if @something_went_wrong
panic("Unrecoverable error!")
fin if.
fin action.

Check conditions in debug builds:

assert @value > 0
assert @result != nil, "Result should not be nil"

Draft eliminates null pointer exceptions. Use Option instead:

@maybe: Option<String> = none
action find(@id: Int) -> Option<User>
return none
fin action.

Continue to Async for asynchronous programming.