Failures
A payment can be declined. The function says so, and the caller has to decide what happens. Quill will not let the failure disappear.
fn charge(amount: USD) -> text
fails Declined
when
amount <= 0 USD -> fail Declined
else -> return "paid"
var receipt = charge(250 USD)
on Declined -> say "Card declined"
say receipt
paid
fails Declined goes at the top of the function, right under fn. A failure’s name starts with a capital letter. fail Declined leaves the function with that failure. It does not give the text back.
Under the call, on Declined says what to do. Every failure named in fails needs its own on. When the call succeeds, Quill skips the on lines and continues. receipt holds "paid", and the next line says it.
When the call fails, the on line runs and the program stops. The lines after the call do not run, so they cannot use a receipt that was never created:
fn charge(amount: USD) -> text
fails Declined
when
amount <= 0 USD -> fail Declined
else -> return "paid"
var receipt = charge(0 USD)
on Declined -> say "Card declined"
say receipt
Card declined
There is no paid line. The charge was declined, the on line said so, and the program stopped.
Carrying on with a value
Stopping is right when nothing sensible can happen next. Often something can: a declined card still deserves a receipt that says so. Put a value after -> instead of a statement. That value takes the place of the call’s answer, and the program carries on:
fn charge(amount: USD) -> text
fails Declined
when
amount <= 0 USD -> fail Declined
else -> return "paid"
receipt = charge(0 USD)
on Declined -> "declined"
say "Receipt: {receipt}"
say "Thank you for shopping"
Receipt: declined
Thank you for shopping
Read it out loud: “receipt is the charge, or on Declined, declined.” The value has to be the same kind the call gives back. charge gives back text, so on Declined -> 0 is a mistake:
fn charge(amount: USD) -> text
fails Declined
when
amount <= 0 USD -> fail Declined
else -> return "paid"
receipt = charge(0 USD)
on Declined -> 0
Problem on line 8 of example.quill:
on Declined -> 0
^
charge gives back Text, but 0 is a Number.
The value after -> takes the place of the call's answer, so it has to be text too.
Each on chooses for itself. One failure can give a value while another stops:
fn charge(amount: USD) -> text
fails Declined, NetworkDown
when
amount <= 0 USD -> fail Declined
amount > 1000 USD -> fail NetworkDown
return "paid"
receipt = charge(5000 USD)
on Declined -> "declined"
on NetworkDown -> say "The bank is offline. Try again later."
say "Receipt: {receipt}"
The bank is offline. Try again later.
So the rule after -> is: a statement like say runs and then stops, and a value is used and the program carries on. A call on its own line gives nothing back, so under it on always runs a statement.
A function can pass a failure upward instead of handling it. try is allowed only inside a function that fails the same way:
fn charge(amount: USD) -> text
fails Declined
when
amount <= 0 USD -> fail Declined
else -> return "paid"
fn checkout(amount: USD) -> text
fails Declined
return try charge(amount)
var receipt = checkout(250 USD)
on Declined -> say "Card declined"
say receipt
paid
checkout declares Declined too, so try charge(amount) can pass that failure on. The caller of checkout still handles it.
Calling charge with no on and no try is a mistake. Quill names the failure you forgot.
try goes where the call’s answer is used: after return, = or say. A call that gives nothing back, like write_file, stands on its own line, and try can’t stand there. Pass its failure upward with on instead:
permit write "out"
fn save(note: text)
fails CantWrite
try write_file("out/note.txt", note)
permit write "out"
fn save(note: text)
fails CantWrite
write_file("out/note.txt", note)
on CantWrite -> fail CantWrite
on CantWrite -> fail CantWrite does what try would do: save leaves with the same failure, and its caller handles it.
Try it
- Charge
0 USDand handleDeclined. - Add
fails NetworkDownand a secondon. Forget one of them and read what Quill says. - Move the charge into a second function with
try, and handleDeclinedat the call. - Change
on Declined -> say "Card declined"toon Declined -> "declined", and add asayline after the call. Which version reaches that line?