tryScope<T> static method
Part of the parallel of Rust's try operator that handles the NonePropagation thrown by the method try_. When a NonePropagation is handled, a None is returned.
To ensure the propagation is caught somewhere and to prevent incompatible
Err types (thus ensuring compile-time safety), an instance of
NonePropagationToken
is already provided as the fn
argument.
To prevent compromising the None propagation strategy, keep the token
within the body of the fn
callback and other local functions in it
(thus, avoid for example copying it to a global variable).
This static method will rethrow any non-NonePropagation
error or exception thrown in fn
.
Synchronous
Behaves identically to Option.asyncTryScope, but synchronously,
thus returning Option<T>
rather than Future<Option<T>>
.
Try-catch warning
Using try catch in combination with try_ can be done,
however thrown Propagation
s should be
handled only by the nidula
library, and not — by accident — by your
application.
See also: Nidula warning: None/Err propagation with try-catch blocks.
Examples
Example 1:
In this example, Option.tryScope
returns an Option<int>
.
If the passed argument l
is Some
, example1
returns a Some
;
else if the passed argument l
is None
, example1
returns None
.
Option<int> example1(Option<int> l) {
return Option.tryScope<int>((nt) {
l = Some(l.try_(nt) + [1, 2, 3].elementAt(1)); // it will propagate now if initial `l` was None
return Some(l.try_(nt)); // it will return a Some if initial `l` was Some
});
}
Example 2:
In the next example, Option.tryScope
always returns a
None<int>
.
Option<int> example2(Option<int> l) {
return Option.tryScope<int>((nt) {
l = Some(l.try_(nt) + [1, 2, 3].elementAt(1)); // it will propagate now if initial `l` was None
l = None(); // not propagating yet
l.try_(nt); // it will propagate now if initial `l` was Some
l = Some(l.try_(nt) + [5, 6].elementAt(1)); // dead code (not detected by IDE)
return Some(l.try_(nt));
});
}
Example 3:
The following example, we also want to repackage any RangeError that are thrown in the body of Option.tryScope. While this functionality can be useful in specific cases, it's important to note that it deviates from the primary purpose of the parallel of the try operator.
Option<int> example3(Option<int> l) {
try {
return Option.tryScope<int>((nt) {
l = Some(l.try_(nt) + [1, 2, 3].elementAt(100)); // it will propagate now if initial `l` was None, else it will throw a RangeError
l = None(); // dead code (not detected by IDE)
l.try_(nt);
l = Some(l.try_(nt) + [5, 6].elementAt(1));
return Some(l.try_(nt));
});
} on RangeError {
return None();
}
}
Implementation
static Option<T> tryScope<T>(Option<T> Function(NonePropagationToken nt) fn) {
try {
return fn(const NonePropagationToken._());
} on NonePropagation {
return None();
}
}