Errors
error is a shorthand declaration for typed errors that flow as effects.
Declaration
pub error io_err:
not_found path
denied path
invalid_path path
failed (path, str)This generates several pieces at once:
- A
pub enum io_errwhose variants arenot_found path,denied path,invalid_path path, andfailed (path, str). - A
pub act io_errwhose operations share the variant names and returnnever. - An
impl Throw io_errwithtype throws = '[io_err]andour e.throwthat performs the matching effect operation. - An
impl Display io_errwith a default text rendering (overridable with a hand-written impl). io_err::wrapin the companion module, which closes the error effect into aresultvalue.- An
uphelper in the companion module when the declaration containsfromentries. It lifts the linked narrower errors into the declared error type.
Constructors and operations share names
Each variant name is both a data constructor and an effect operation. The surrounding context picks the right one.
my err: io_err = io_err::not_found path // built as a value
io_err::not_found path // raised as an effectRaising with fail
fail is a prelude prefix operator defined as a transparent wrapper around e.throw:
pub prefix(fail) = \e -> e.throwUse it to surface a constructed error value into the effect row.
my missing path = fail (io_err::not_found path)Calling missing performs io_err::not_found; fail makes that error visible in the effect row.
Catching by name
catch arms handle errors by naming each operation directly.
my read_text_or_label path = catch read_text path:
io_err::not_found _, _ -> "(missing)"
io_err::denied _, _ -> "(denied)"
value -> valueYulang's error story is built on catching by name. There is no type-erased catch-all and no runtime dispatch over arbitrary Display instances; Yulang intentionally does not provide an anyhow-style boundary. Each error keeps its concrete type in the effect row, so the origin and the handler of every error are visible from types alone.
wrap: closing into a value
my read_text_safe path =
my wrapped = io_err::wrap: read_text path
case wrapped:
result::ok text -> text
result::err err -> err.showE::wrap catches the matching error effect produced by its thunk argument and returns result _ E. When E has from entries, wrap also catches the linked narrower errors and wraps them through the generated conversions.
from aggregation
The following excerpt assumes that a parser module defines parse_err and parse_json:
pub error app_err:
file from io_err
parse from parse_errThis generates:
- variants
app_err::file io_errandapp_err::parse parse_err. - generated conversions from
io_errandparse_errtoapp_err. - an extended
app_err::wrapthat also catchesio_errandparse_err. app_err::up, a handler that turns the narrower errors intoapp_err.
my read_and_parse path =
app_err::up:
my text = read_text path // [io_err]
parse_json text // [parse_err]
// the block as a whole has effect [app_err]See also Casts for the underlying conversion machinery and Effects for the general catch and effect-row story.