Class: MonadOxide::Result

Inherits:
Object
  • Object
show all
Defined in:
lib/result.rb

Overview

A Result is a chainable series of sequential transformations. The Result structure contains an ‘Ok<A> | Err<Exception>`. This is the central location for documentation between both `Ok` and `Err`. It is best to think of any given `Ok` or `Err` as a `Result` instead. All methods on `Ok` are present on `Err` and vice versa. This way we can interchange one for the other during virtually any call.

This is a base-class only, and you should never see instances of these in the wild.

Direct Known Subclasses

Err, Ok

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data) ⇒ Result

Returns a new instance of Result.

Raises:

  • (NoMethodError)


83
84
85
# File 'lib/result.rb', line 83

def initialize(data)
  raise NoMethodError.new('Do not use Result directly. See Ok and Err.')
end

Class Method Details

.try(f = nil) { ... } ⇒ MonadOxide::Result<A, E>

Convenience for executing arbitrary code and coercing it to a Result. Any exceptions raised coerce it into an Err, and the return value is the value for the Ok. raised, and an Err with the exception if any exception is raised.

Parameters:

  • f (Proc<Result<A>>) (defaults to: nil)

    The function to call. Could be a block instead. Takes no arguments and could return any [Object].

Yields:

  • Will yield a block that takes no arguments A and returns a [Object]. Same as ‘f’ parameter.

Returns:



73
74
75
76
77
78
79
# File 'lib/result.rb', line 73

def try(f=nil, &block)
  begin
    MonadOxide.ok((f || block).call())
  rescue => e
    MonadOxide.err(e)
  end
end

Instance Method Details

#and_then(f = nil) { ... } ⇒ Ok<B> | Err<C>

For ‘Ok’, invokes ‘f’ or the block with the data and returns the Result returned from that. Exceptions raised during ‘f’ or the block will return an ‘Err<Exception>’.

For ‘Err’, returns itself and the function/block are ignored.

This method is used for control flow based on ‘Result’ values.

‘and_then’ is desirable for chaining together other Result based operations, or doing transformations where flipping from an ‘Ok’ to an ‘Err’ is desired. In cases where there is little/no risk of an ‘Err’ state, @see Result#map.

The ‘Err’ equivalent operation is @see Result#or_else.

The return type is enforced.

Parameters:

  • f (Proc<A, Result<B>>) (defaults to: nil)

    The function to call. Could be a block instead. Takes an [A=Object] and must return a [Result<B>].

Yields:

  • Will yield a block that takes an A and returns a Result<B>. Same as ‘f’ parameter.

Returns:

  • (Ok<B> | Err<C>)

    A new Result from ‘f’ or the block. Exceptions raised will result in ‘Err<C>’. If ‘f’ returns a non-Result, this will return ‘Err<ResultReturnExpectedError>’.



119
120
121
# File 'lib/result.rb', line 119

def and_then(f=nil, &block)
  Err.new(ResultMethodNotImplementedError.new())
end

#err?Boolean

Determine if this is a MonadOxide::Err.

Returns:

  • (Boolean)

    ‘true` if this is a MonadOxide::Err, `false` otherwise.



90
91
92
# File 'lib/result.rb', line 90

def err?()
  false
end

#flattenResult

Un-nest this ‘Result’. This implementation is shared between ‘Ok’ and ‘Err’. In both cases, the structure’s data is operated upon. return the inner-most ‘Result’, regardless of the depth of nesting. Otherwise return ‘self’.

Returns:

  • (Result)

    If ‘A’ is a ‘Result’ (meaning this ‘Result` is nested),



143
144
145
146
147
148
149
# File 'lib/result.rb', line 143

def flatten()
  if @data.kind_of?(Result)
    return @data.flatten()
  else
    self
  end
end

#inspect_err(f = nil) { ... } ⇒ Result<A>

In the case of ‘Err’, applies ‘f’ or the block over the ‘Exception’ and returns the same ‘Err’. No changes are applied. This is ideal for logging. For ‘Ok’, this method falls through. Exceptions raised during these transformations will return an ‘Err’ with the Exception.

Parameters:

  • f (Proc<A, B>) (defaults to: nil)

    The function to call. Could be a block instead. Takes an [A=Exception] the return is ignored.

Yields:

  • Will yield a block that takes an A the return is ignored. Same as ‘f’ parameter.

Returns:

  • (Result<A>)

    returns self.



133
134
135
# File 'lib/result.rb', line 133

def inspect_err(f=nil, &block)
  Err.new(ResultMethodNotImplementedError.new())
end

#inspect_ok(f = nil) { ... } ⇒ Result<A>

In the case of ‘Ok’, applies ‘f’ or the block over the data and returns the same ‘Ok’. No changes are applied. This is ideal for logging. For ‘Err’, this method falls through. Exceptions raised during these transformations will return an ‘Err’ with the Exception.

Parameters:

  • f (Proc<A, B>) (defaults to: nil)

    The function to call. Could be a block instead. Takes an [A] the return is ignored.

Yields:

  • Will yield a block that takes an A the return is ignored. Same as ‘f’ parameter.

Returns:

  • (Result<A>)

    returns self.



161
162
163
# File 'lib/result.rb', line 161

def inspect_ok(f=nil, &block)
  Err.new(ResultMethodNotImplementedError.new())
end

#map(f = nil) { ... } ⇒ Result<B>

In the case of ‘Ok’, applies ‘f’ or the block over the data and returns a new new ‘Ok’ with the returned value. For ‘Err’, this method falls through. Exceptions raised during these transformations will return an ‘Err’ with the Exception.

Parameters:

  • f (Proc<A, B>) (defaults to: nil)

    The function to call. Could be a block instead. Takes an [A=Object] and returns a B.

Yields:

  • Will yield a block that takes an A and returns a B. Same as ‘f’ parameter.

Returns:

  • (Result<B>)

    A new ‘Result<B>’ whose ‘B’ is the return of ‘f’ or the block. Errors raised when applying ‘f’ or the block will result in a returned ‘Err<Exception>’.



177
178
179
# File 'lib/result.rb', line 177

def map(f=nil, &block)
  Err.new(ResultMethodNotImplementedError.new())
end

#map_err(f = nil) { ... } ⇒ Result<B>

In the case of ‘Err’, applies ‘f’ or the block over the ‘Exception’ and returns a new new ‘Err’ with the returned value. For ‘Ok’, this method falls through. Exceptions raised during these transformations will return an ‘Err’ with the Exception.

Parameters:

  • f (Proc<A, B>) (defaults to: nil)

    The function to call. Could be a block instead. Takes an [A=Exception] and returns a [B=Exception].

Yields:

  • Will yield a block that takes an A and returns a B. Same as ‘f’ parameter.

Returns:

  • (Result<B>)

    A new ‘Result<A, ErrorB>’ whose ‘ErrorB’ is the return of ‘f’ or the block. Errors raised when applying ‘f’ or the block will result in a returned ‘Err<Exception>’.



193
194
195
# File 'lib/result.rb', line 193

def map_err(f=nil, &block)
  Err.new(ResultMethodNotImplementedError.new())
end

#match(matcher) ⇒ R

Use pattern matching to work with both Ok and Err variants. This is useful when it is desirable to have both variants handled in the same location. It can also be useful when either variant can coerced into a non-Result type.

Ruby has no built-in pattern matching, but the next best thing is a Hash using the Result classes themselves as the keys.

Tests for this are found in Ok and Err’s tests.

upon.

Parameters:

  • matcher (Hash<Class, Proc<T | E, R>] matcher The matcher to match)

    atcher [Hash<Class, Proc<T | E, R>] matcher The matcher to match

Options Hash (matcher):

  • MonadOxide::Ok (Proc)

    The branch to execute for Ok.

  • MonadOxide::Err (Proc)

    The branch to execute for Err.

Returns:

  • (R)

    The return value of the executed Proc.



213
214
215
# File 'lib/result.rb', line 213

def match(matcher)
  matcher[self.class].call(@data)
end

#ok?Boolean

Determine if this is a MonadOxide::Ok.

Returns:

  • (Boolean)

    ‘true` if this is a MonadOxide::Ok, `false` otherwise.



220
221
222
# File 'lib/result.rb', line 220

def ok?()
  false
end

#or_else(f = nil) { ... } ⇒ Ok<B> | Err<C>

For ‘Err’, invokes ‘f’ or the block with the data and returns the Result returned from that. Exceptions raised during ‘f’ or the block will return an ‘Err<Exception>’.

For ‘Ok’, returns itself and the function/block are ignored.

This method is used for control flow based on ‘Result’ values.

‘or_else’ is desirable for chaining together other Result based operations, or doing transformations where flipping from an ‘Ok’ to an ‘Err’ is desired. In cases where there is little/no risk of an ‘Err’ state, @see Result#map.

The ‘Ok’ equivalent operation is @see Result#and_then.

The return type is enforced.

Parameters:

  • f (Proc<A, Result<B>>) (defaults to: nil)

    The function to call. Could be a block instead. Takes an [A=Object] and must return a [Result<B>].

Yields:

  • Will yield a block that takes an A and returns a Result<B>. Same as ‘f’ parameter.

Returns:

  • (Ok<B> | Err<C>)

    A new Result from ‘f’ or the block. Exceptions raised will result in ‘Err<C>’. If ‘f’ returns a non-Result, this will return ‘Err<ResultReturnExpectedError>’.



249
250
251
# File 'lib/result.rb', line 249

def or_else(f=nil, &block)
  Err.new(ResultMethodNotImplementedError.new())
end

#unwrapA

Dangerously access the ‘Result’ data. If this is an ‘Err’, an exception will be raised. It is recommended to use this for tests only.

Returns:

  • (A)
    • The inner data of this ‘Ok’.

Raises:



258
259
260
# File 'lib/result.rb', line 258

def unwrap()
  Err.new(ResultMethodNotImplementedError.new())
end

#unwrap_errE

Dangerously access the ‘Result’ data. If this is an ‘Ok’, an exception will be raised. It is recommended to use this for tests only.

Returns:

  • (E)
    • The inner data of this ‘Err’.

Raises:



267
268
269
# File 'lib/result.rb', line 267

def unwrap_err()
  Err.new(ResultMethodNotImplementedError.new())
end

#unwrap_err_or_else(_f) { ... } ⇒ T|B

Safely unwrap the ‘Result` but with lazy evaluation. In the case of `Err`, this returns the wrapped value. For `Ok` the function provided is evaluated and its returned value is what is returned.

Parameters:

  • f (Proc<B>)

    The function to call for ‘Ok`. Could be a block instead. Takes nothing and returns a [B=Object].

Yields:

  • Will yield a block for ‘Ok` that takes nothing and returns a [B=Object]. Same as `f’ parameter.

Returns:

  • (T|B)

    The wrapped value for ‘Err` and the returned from `f` for `Ok`.

Raises:



282
283
284
# File 'lib/result.rb', line 282

def unwrap_err_or_else(_f)
  raise ResultMethodNotImplementedError.new()
end

#unwrap_or(_) ⇒ T|B

Attempt to safely access the ‘Result` data. This always returns a value instead of raising an Exception. In the case of `Ok`, the data is returned. In the case of `Err`, the value provided is returned.

Parameters:

  • _ (B)

    The value to use for ‘Err`.

Returns:

  • (T|B)

    The inner data of this ‘Ok` or the passee value.



292
293
294
# File 'lib/result.rb', line 292

def unwrap_or(_)
  Err.new(ResultMethodNotImplementedError.new())
end

#unwrap_or_else(_f) { ... } ⇒ T|B

Safely unwrap the ‘Result` but with lazy evaluation. In the case of `Ok`, this returns the wrapped value. For `Err` the function provided is evaluated and its returned value is what is returned.

Parameters:

  • f (Proc<B>)

    The function to call for ‘Err`. Could be a block instead. Takes nothing and returns a [B=Object].

Yields:

  • Will yield a block for ‘Err` that takes nothing and returns a [B=Object]. Same as `f’ parameter.

Returns:

  • (T|B)

    The wrapped value for ‘Ok` and the returned from `f` for `Err`.

Raises:



307
308
309
# File 'lib/result.rb', line 307

def unwrap_or_else(_f)
  raise ResultMethodNotImplementedError.new()
end