A result encapsulation class, like the Either type in Haskell.

Context Quick Starting Guide


    Result = Lab42::Result

    let(:ok) { Result.ok(42) }
    let(:error) { Result.error("oh no!") }

Accessing a result


Example: It's ok

  expect( ok ).to be_ok

Example: Its value might be of interest

  expect( ok.value ).to eq(42)

Example: And will not raise any error

  expect( ok.raise! ).to eq(42)

If Error

Example: It's h(error)

  expect( error ).not_to be_ok

Example: Its value might (still) be of interest

  expect( error.value ).to eq("oh no!")

Example: And will certainly raise this time

  expect{ error.raise! }.to raise_error(RuntimeError, "oh no!") 

Capturing Exceptions

If you have a piece of code like this:

  rescue MyError
    Result.error("Oh my", error: MyError)

you can replace it with the convenient

    Result.from("Oh my") {some_computation}


  def divide by
    Result.from("Zero Division") { 100 / by }

Example: Zero Division, not a problem anymore

  error = divide(0)
  expect( error ).not_to be_ok
  expect( error.status ).to eq(ZeroDivisionError)

Example: Correct Division still works

    divide(4) in [_, value]
    expect( value ).to eq(25)

Context A More Detailed View

Ok without a value

Sometimes all we want is an :ok to get back or :error with a message, than we can use the default value of nil

Example: Nil is default for ok

   expect(Result.ok.value).to be_nil
   expect( Result.ok.ok? ).to be_truthy

Error with a different exception

On the other hand you might not want to raise a RuntimeError all the time, that is when the optional keyword parameter error: comes in handy

Example: Error with an Argument

    expect{ Result.error("ooops", error: ArgumentError).raise!}
      .to raise_error(ArgumentError, "ooops")

Context Pattern Matching


    let(:my_error) {}
    let(:message) {"That was bad"}
    let(:surprise) {"Not 42"}
    let(:ok) {Result.ok(surprise)}
    let(:error) {Result.error(message, error: my_error)}

    def match result
      case result
      in [:ok, value]
      in [my_error, message]

Example: Matching The Good

    expect( match(ok) ).to eq(surprise)

Example: Matching The Bad (no, there will be no Ugly)

    expect( match(error) ).to eq(message)

Example: If the Exception I do not want

    error in [_, error_message]
    expect( error_message ).to eq(message)

Context Saveguards

And last but not least, to assure that all instances of Result are frozen we have removed the default constructor (we have not - yet - shadowed Object#allocate though)

Example: Look Mam, no default constructor!

    expect{ }.to raise_error(NoMethodError)


Copyright 2020 Robert Dober [email protected]

Apache-2.0 c.f LICENSE