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 Results 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 NoneResult[E, T] into Option[T], mapping
Ok(v) to Some(v) and Err(e) to NoneResult[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.OpsResults.
This documentation began as a derivative of the Rust Result<T, E> documentation