[Raku] `trys`

Run Settings
LanguageRaku
Language Version
Run Command
# See https://stackoverflow.com/questions/51644197 use lib '.'; # `trys` in detail use X2; # `trys` tries a list of callables, short circuiting if one "works": say trys {die}, {42}, {fail} # 42 # By default, "works" means no exception thrown and result is not a `Failure`: say trys {die}, {fail}, {42} # 42 # An (optional) `:reject` argument lets you specify # value(s) you want rejected if they smartmatch: say trys :reject(Nil,/o/), {Nil}, {'no'}, {2} # 2 # If all callables throw, return `Failure` wrapping exceptions(s): say trys :reject(Nil), {Nil} # (HANDLED) Rejected Nil say trys {die} # (HANDLED) Died say trys {(42/0).Str} # (HANDLED) Attempt to divide by zero # Specify `:!HANDLED` if the returned `Failure` is to be left unhandled: say (trys {(42/0).Str}, :!HANDLED) .handled; # False # The first callable is passed the caller's current exception as its topic: $! = X::AdHoc.new: payload => 'foo'; trys {.say} # foo # Topic of subsequent callables is exception from prior failed callable: trys {die 'bar'}, *.say; # bar trys {fail 'bar'}, {die "$_ baz"}, *.say; # bar baz # Caller's `$!` is left alone (presuming no `trys` bug): say $!; # foo # To include *all* throws in `Failure`, specify `:all-throws`: say trys {die 1}, {die 2}, :all-throws; # (HANDLED) foo 1 2 # Note the `foo` -- `all-throws` includes the caller's original `$!`. ``` # `trys` "traps" ``` # Some "traps" are specific to the way `trys` works: say trys { ... } // 42; # "(HANDLED) Stub code executed" say trys { ... }, { 42 } # 42 <-- List of blocks, no `//`. #trys 22; # Type check failed ... got Int (22) say trys { 22 } # 22 <-- Block, not statement. #trys {} # Type check failed ... got Hash ({}) say trys {;} # Nil <-- Block, not Hash. # Other "traps" are due to the way Raku works: # WAT `False` result if callable has `when`s but none match: say do {when rand { 42 }} # False <-- It's how Raku works. say trys {when rand { 42 }} # False <-- So same with `trys`. say trys {when rand { 42 }; Nil} # Nil <-- Succinct fix. say trys {when rand { 42 }; default {}} # Nil <-- Verbose fix. # Surprise `(Any)` result if callable's last/return value is explicitly `$!`: $! = X::AdHoc.new: payload => 'foo'; say try {$!} # (Any) <-- Builtin `try` clears `$!`. say $!; # (Any) <-- Caller's too! $! = X::AdHoc.new: payload => 'foo'; say trys {$!} # (Any) <-- `trys` clears `$!` BUT: say $!; # foo <-- Caller's `$!` left alone. $! = X::AdHoc.new: payload => 'foo'; say try {$!.self} # foo <-- A fix with builtin `try`. say $!; # (Any) <-- Caller's `$!` still gone. $! = X::AdHoc.new: payload => 'foo'; say trys {.self} # foo <-- Similar fix with `trys`. say $!; # foo <-- Caller's `$!` left alone.
# See https://stackoverflow.com/questions/51644197/ unit module X2; our sub trys ( **@callables, #= List of callables. :$reject = (), #= Value(s) to be rejected. :$all-throws = False, #= Return *all* thrown exceptions? :$HANDLED = True, #= Mark returned `Failure` handled? ) is export { my @throws; #= For storing all throws if `$all-throws`. $! = CLIENT::<$!>; # First callable's `$!` is `trys` caller's. @throws.push: $! if $! && $all-throws; # Include caller's `$!` in list of throws. my $result is default(Nil); # At least temporarily preserve a `Nil` result. for @callables -> &callable { $result = try { callable $! } # `try` next callable, passing `$!` from prior callable as topic. if not $! and $result ~~ $reject.any # Promote result to exception? { $! = X::AdHoc.new: payload => "Rejected $result.gist()" } @throws.push: $! if $! && $all-throws; return $result if not $!; # Return result if callable didn't throw. } $! = X::AdHoc.new: payload => @throws if $all-throws; given Failure.new: $! { # Convert exception(s) to `Failure`. .handled = $HANDLED; .return } }
Editor Settings
Theme
Key bindings
Full width
Lines