Result-rb

A ruby port of the Rust result-monad Result.

Installation

Add this line to your application's Gemfile:

gem 'rresult'

Usage

The Result can be an Ok, to wrap successfull values, or an Err, to wrap something that went wrong.

An Err has a description, this should be a Symbol, and a value, which can be any Object

Constructors

Regular class-constructors:

ok_result = Result::ok(42)

err_result = Result::err(description: :not_the_answer_to_anything, value: 41)

Object is monkey-patched to provide convenience-constructors

ok_result = Ok(42)

err_result = Err(:not_the_answer_to_anything, 41)

Some inspectors

Check if the Result is Ok or Err

Ok(42).ok? == true
Ok(42).is_ok == true

Err(:not_the_answer_to_anything, 41).err? == true
Err(:not_the_answer_to_anything, 41).is_err == true

Check the wrapped value inside:

Ok(42).contains? 42 == true
Ok(42).contains 42  == true

Err(:not_the_answer_to_anything, 41).contains_err?(:not_the_answer_to_anything, 42) == true
Err(:not_the_answer_to_anything, 41).contains_err(:not_the_answer_to_anything, 42)  == true

Accessors

Get the wrapped error elements with #err_description and #err_value. These return nil for an Ok.

Ok(42).err_description                               == nil
Ok(42).err_value                                     == nil
Err(:not_the_answer_to_anything, 41).err_description == :not_the_answer_to_anything
Err(:not_the_answer_to_anything, 41).err_value       == 41

Unwrappers

#unwrap return the wrapped value of an Ok, but raises a RuntimeError for an Err

Ok(42).unwrap == 42
Err(:wrong_answer, 41).unwrap # raises RuntimeError

#unwrap_err returns an Array with the description and value of an Err, but raises a RuntimeError for an Ok

Ok(42).unwrap # raises RuntimeError
Err(:wrong_answer, 41).unwrap_err == [:wrong_answer, 41]

#unwrap_or unwraps an Ok or returns the provided default if Err

Ok(42).unwrap_or(-1)                               == 42
Err(:not_the_answer_to_anything, 41).unwrap_or(-1) == -1

#unwrap_or_else unwraps an Ok or returns the provided default if Err as result of the block. The block takes the error description and the error value

Ok(42).unwrap_or_else { |descr, val| val / 2 }                              == 42
Err(:not_the_answer_to_anything, 41).unwrap_or_else{ |descr, val| val / 2 } == 20

Map

#map allows to apply a block to the wrapped value, if its Ok. It does not touch an Err

Ok(42).map { |val| val * 2 } == Ok(84)

Err(:not_the_answer_to_anything, 41).map { |val| val * 2 } == Err(:not_the_answer_to_anything, 41)

#map_or unwraps the Result and returns a default value, in case of an Err

Ok(42).map_or(-1) { |val| val * 2 } == 84

Err(:not_the_answer_to_anything, 41).map_or(-1) { |val| val * 2 } == -1

#map_or_else unwraps the Result like #map_or, but take 2 lambda's to specify the mapping of both Ok and Err

map     = ->(ok_val)         { ok_val * 2 }
or_else = ->(_desc, err_val) { err_val / 2 }

Ok(42).map_or_else(map: map, or_else: or_else) == 84
Err(:not_the_answer_to_anything, 41).map_or_else(map: map, or_else: or_else) == 20

#map_err maps an Err with a provided block. That block should return an Array with the new description and value

Ok(42).map_err { |descr, err_val| val * 2 } == Ok(84)

Err(:not_the_answer_to_anything, 41).map { |val| val * 2 } == Err(:not_the_answer_to_anything, 41)

Logical AND combinator

#and replaces the value of the Result by that of that argument, only if the Result is an Ok

some_ok_res = Ok(-1)
some_err_res = Err(:mistake, 'you did something wrong')

Ok(42).and(some_ok_res)                                == some_ok_res
Ok(42).and(some_err_res)                               == some_ok_res
Err(:not_the_answer_to_anything, 41).and(some_ok_res)  == Err(:not_the_answer_to_anything, 41)
Err(:not_the_answer_to_anything, 41).and(some_err_res) == Err(:not_the_answer_to_anything, 41)

#and_then applies a logical AND with the result of a block

Ok(42).and_then { |val| Ok(val * 2) }).to be == Ok(84)
Ok(42).and_then { |val| Err(:wrong_answer, 41) } == Err(:wrong_answer, 41)

Logical OR combinator

#or replaces the value of the Result by that of that argument, only if the Result is an Err

some_ok_res = Ok(-1)
some_err_res = Err(:mistake, 'you did something wrong')

Ok(42).or(some_ok_res)                                == Ok(42)
Ok(42).and(some_err_res)                              == Ok(42)
Err(:not_the_answer_to_anything, 41).or(some_ok_res)  == some_ok_res
Err(:not_the_answer_to_anything, 41).or(some_err_res) == some_err_res

#or_else applies a logical OR with the result of a block. The block provides takes the description and value of the Err

Err(:wrong_answer, 41).or_else { |descr, val| Ok(val / 2) }).to be      == Ok(20)
Err(:wrong_answer, 41).or_else { |descr, val| Err(:not_corrected, -1) } == Err(:not_corrected, -1)