package extensions
- Alphabetic
- Public
- Protected
Error handling with the Result type.
Error handling with the Result type.
Result
is a type used for returning and propagating errors. It is an
disjoint union with the variants, Ok, representing success and
containing a value, and Err, representing error and containing an error
value.
Functions should return Result
whenever errors are expected and
recoverable.
For simplicity many examples make use of primitives such as String
and
Int
for the error type. It is recommended that in practice developers
should try to make use of more structured types to allow for improved error
handling. As opposed to relying on a stringly-typed interface or integer
error codes.
A simple function returning Result
might be defined and used like so:
>>> import dev.jsbrucker.result._ >>> sealed trait MajorVersion >>> object MajorVersion { ... case object V1 extends MajorVersion ... case object V2 extends MajorVersion ... } >>> sealed trait ParseError >>> object ParseError { ... case object InvalidHeaderLength extends ParseError ... case object UnsupportedVersion extends ParseError ... } >>> def parseMajorVersion(header: List[Int]): Result[ParseError, MajorVersion] = ... header.headOption match { ... case None => Err(ParseError.InvalidHeaderLength) ... case Some(1) => Ok(MajorVersion.V1) ... case Some(2) => Ok(MajorVersion.V2) ... case _ => Err(ParseError.UnsupportedVersion) ... } >>> val version = parseMajorVersion(List(1, 2, 3, 4)) >>> version match { ... case Ok(v) => "working with version: " + v.toString ... case Err(e) => "error parsing header: " + e.toString ... } working with version: V1
Pattern matching on Result
s is clear and straightforward for simple cases,
but Result
comes with some convenience methods that make working with it
more succinct.
>>> import dev.jsbrucker.result._ >>> val goodResult: Result[String, Int] = Ok(10); >>> val badResult: Result[String, Int] = Err("Some Error") // The `isOk` and `isErr` methods do what they say. >>> goodResult.isOk && !goodResult.isErr true >>> badResult.isErr && !badResult.isOk true // `map` replaces the `Ok` value of a `Result` with the result of the provided function >>> goodResult.map(_ + 1) Ok(11) // `map` leaves an `Err` value of a `Result` as it was, ignoring the provided function >>> badResult.map(_ - 1) Err(Some Error) // Use `andThen` to continue the computation. scala> goodResult.andThen(i => Ok(i == 11)) res0: Result[String, Boolean] = Ok(false) // Use `orElse` to handle the error. scala> badResult.orElse { | case "Anticipated Error" => Ok(0) | case "Some Error" => Err(true) | case _ => Err(false) | } res1: Result[Boolean, Int] = Err(true)
In addition to working with pattern matching, Result
provides a wide
variety of different methods.
The isOk and isErr methods return true
if
the Result
is Ok
or Err
, respectively.
The isOkAnd and isErrAnd methods take
in a predicate and return true
if the Result
is Ok
or Err
respectively, and the predicate returns true
when applied to the contained
value.
The contains and containsErr
methods take in a value and return true
if it matches the inner Ok
or
Err
value respectively.
These methods transform Result
to Option
:
Result[E, T]
into Option[E]
, mapping
Err(e)
to Some(e)
and Ok(v)
to None
Result[E, T]
into Option[T]
, mapping
Ok(v)
to Some(v)
and Err(e)
to None
Result[E,
Option[T]]
into an Option[Result[E, T]]
Result[Option[E], T]
into an Option[Result[E, T]]
This method transforms the contained value of the Ok
variant:
Result[E, T]
into Result[E, U]
by
applying the provided function to the contained value of Ok
and
leaving Err
values unchangedThis method transforms the contained value of the Err
variant:
Result[E, T]
into Result[F, T]
by applying the provided function to the contained value of Err
and
leaving Ok
values unchangedThese methods transform a Result[E, T]
into a value of a possibly
different type U
:
Ok
or Err
respecitively,
or returns the provided default value.Ok
or Err
respectively, or applies the provided default fallback function to the
contained value of Err
or Ok
respectively.These methods transform Result
to Future
:
Result[E,
Future[T]]
into a Future[Result[E, T]]
Result[Future[E], T]
into a Future[Result[E, T]]
These methods extract the contained value in a Result[E, T]
when it is the
Ok
variant. If the Result
is Err
:
These methods extract the contained value in a Result[E, T]
when it is the
Err
variant. If the Result
is Ok
:
These methods treat the Result
as a boolean value, where Ok
acts like
true
and Err
acts like false
. There are two categories of these
methods: ones that take a Result
as input, and ones that take a function
as input (to be lazily evaluated).
The and and or methods take another Result
as
input, and produce a Result
as output. The and
method can produce a
Result[E, U]
value having a different inner type U
than Result[E, T]
.
The or
method can produce a Result[F, T]
value having a different error
type F
than Result[E, T]
.
method | self | input | output |
---|---|---|---|
| | (ignored) | |
| | | |
| | | |
| | | |
| | | |
| | (ignored) | |
The andThen and orElse methods take a
function as input, and only evaluate the function when they need to produce
a new value. The andThen
method can produce a Result[E, U]
value having
a different inner type U
than Result[E, T]
. The orElse
method can
produce a Result[F, T]
value having a different error type F
than
Result[E, T]
.
NOTE: flatMap is equivalent to andThen
and it is
provided for consistency with typical Scala conventions. Along those lines
flatMapErr is equivalent to orElse
.
method | self | function input | function result | output |
---|---|---|---|---|
| | (not provided) | (not evaluated) | |
| | | | |
| | | | |
| | | | |
| | | | |
| | (not provided) | (not evaluated) | |
Extension methods are provided to facilitate conversion of several types to
a Result
. They can imported using import dev.jsbrucker.result.implicits._
Option
gets a number of additional helpers. See:
extensions.option.OpsResult
s.
This documentation began as a derivative of the Rust Result<T, E> documentation