Try/exceptions
Unlike PHP, in THP all errors must be explicitly declared and handled.
Declare that a function returns an exception
Possible errors have their own syntax: Type!Error
.
This means: This may be a Type
, or an Error
.
For example, a function that returned a DivisionByZero
may be written like this:
fun invert(Int number) -> Int!DivisionByZero {
if number == 0
{
throw DivisionByZero()
}
return 1 / number
}
thp
In the previous segment, Int!DivisionByZero
denotates
that the function may return either a DivisionByZero
error
or an Int
.
The throw
keyword is used to denotate that an error is being
returned.
Multiple error returns
TODO: properly define syntax, how this interacts with type unions.
Multiple errors are chained with !
.
fun sample() -> Int!Error1!Error2!Error3 {
/* ... */
}
thp
Error handling
The caller must handle all possible errors, they don’t automatically bubble up the stack.
THP provides syntax for handling errors following certain patterns, via try expressions:
Naked try
Use a naked try
when you want to rethrow an error, if there is any.
fun dangerous() -> Int!Exception { // May throw randomly
return if Math.random() < 0.5 { 50 }
else { Exception("Unlucky") }
}
fun run() -> Void!Exception {
// If `dangerous()` throws, the function exits with the same error.
// Otherwise, continues
val result = try dangerous()
print("The result is {result}")
}
val res1 = run() // First, without error
val res2 = run() // Then, an example with error
thp
In the previous example:
- If
dangerous()
returns anException
, this exception will be returned byrun()
; - If
dangerous()
succeedes, its value is assigned toresult
, and the function continues executing.
Try/return
Try/return will return a new value if an expression fails, otherwise will assign the success value and continue.
Try/return will run a function and assign its value if Ok
is found.
Otherwise, it will return a new value specified by the programmer.
fun run() -> Int
{
val result = try dangerous() return 0
// ...
}
thp
In the previous example:
- If
dangerous()
fails, its error will be ignored, and0
will be returned fromrun()
. - If
dangerous()
succeedes, its value will be assigned toresult
, and the function continues executing.
Try/else
Try/else will assign a new value if an expression fails.
fun run(Exception!Int possible_value)
{
val mid = try possible_value else 666
print("The value is: {mid}")
}
run(777) // With an actual value
run(Exception("oh uh")) // With an exception
thp
- If
possible_value
is an error, the value666
is used. - If
possible_value
is not an error, its value will be used.
Either way, the function will continue executing.
Try/catch
Try/catch allows the error to be manually used & handled.
fun run()
{
val result = try dangerous()
catch Exception e
{
// This is run if `dangerous()` throws.
// `e` is the thrown error
// Handle the error
// ...
// Return a new value to be assigned to `result`
0
}
}
thp
A try/catch may have many catch
clauses:
try dangerous()
catch Exception1 e
{...}
catch Exception2 e
{...}
catch Exception3 e
{...}
thp