Class: Functional::Either
- Inherits:
-
Object
- Object
- Functional::Either
- Includes:
- AbstractStruct
- Defined in:
- lib/functional/either.rb
Overview
The Either type represents a value of one of two possible types (a disjoint union). It is an immutable structure that contains one and only one value. That value can be stored in one of two virtual position, left or right. The position provides context for the encapsulated data.
One of the main uses of Either is as a return value that can indicate either success or failure. Object oriented programs generally report errors through either state or exception handling, neither of which work well in functional programming. In the former case, a method is called on an object and when an error occurs the state of the object is updated to reflect the error. This does not translate well to functional programming because they eschew state and mutable objects. In the latter, an exception handling block provides branching logic when an exception is thrown. This does not translate well to functional programming because it eschews side effects like structured exception handling (and structured exception handling tends to be very expensive). Either provides a powerful and easy-to-use alternative.
A function that may generate an error can choose to return an immutable Either object in which the position of the value (left or right) indicates the nature of the data. By convention, a left value indicates an error and a right value indicates success. This leaves the caller with no ambiguity regarding success or failure, requires no persistent state, and does not require expensive exception handling facilities.
Either provides several aliases and convenience functions to facilitate these failure/success conventions. The left and right functions, including their derivatives, are mirrored by reason and value. Failure is indicated by the presence of a reason and success is indicated by the presence of a value. When an operation has failed the either is in a rejected state, and when an operation has successed the either is in a fulfilled state. A common convention is to use a Ruby Exception as the reason. The factory method error facilitates this. The semantics and conventions of reason, value, and their derivatives follow the conventions of the Concurrent Ruby gem.
The left/right and reason/value methods are not mutually exclusive. They can be commingled and still result in functionally correct code. This practice should be avoided, however. Consistent use of either left/right or reason/value against each Either instance will result in more expressive, intent-revealing code.
Instance Attribute Summary
Attributes included from AbstractStruct
Class Method Summary collapse
-
.error(message = nil, clazz = StandardError) ⇒ Either
Create an
Eitherwith the left value set to anExceptionobject complete with message and backtrace. -
.iff(lvalue, rvalue, condition = NO_VALUE) { ... } ⇒ Either
If the condition satisfies, return the given A in left, otherwise, return the given B in right.
-
.left(value) ⇒ Either
Construct a left value of either.
-
.reason ⇒ Either
Construct a left value of either.
-
.right(value) ⇒ Either
Construct a right value of either.
-
.value ⇒ Either
Construct a right value of either.
Instance Method Summary collapse
-
#either(lproc, rproc) ⇒ Object
The catamorphism for either.
-
#left ⇒ Object
(also: #reason)
Projects this either as a left.
-
#left? ⇒ Boolean
(also: #reason?, #rejected?)
Returns true if this either is a left, false otherwise.
-
#right ⇒ Object
(also: #value)
Projects this either as a right.
-
#right? ⇒ Boolean
(also: #value?, #fulfilled?)
Returns true if this either is a right, false otherwise.
-
#swap ⇒ Either
If this is a left, then return the left value in right, or vice versa.
Methods included from AbstractStruct
#each, #each_pair, #eql?, #fields, #inspect, #length, #to_h
Class Method Details
.error(message = nil, clazz = StandardError) ⇒ Either
Create an Either with the left value set to an Exception object complete with message and backtrace. This is a convenience method for supporting the reason/value convention with the reason always being an Exception object. When no exception class is given StandardError will be used. When no message is given the default message for the given error class will be used.
130 131 132 133 134 |
# File 'lib/functional/either.rb', line 130 def error( = nil, clazz = StandardError) ex = clazz.new() ex.set_backtrace(caller) left(ex) end |
.iff(lvalue, rvalue, condition = NO_VALUE) { ... } ⇒ Either
If the condition satisfies, return the given A in left, otherwise, return the given B in right.
201 202 203 204 205 |
# File 'lib/functional/either.rb', line 201 def self.iff(lvalue, rvalue, condition = NO_VALUE) raise ArgumentError.new('requires either a condition or a block, not both') if condition != NO_VALUE && block_given? condition = block_given? ? yield : !! condition condition ? left(lvalue) : right(rvalue) end |
.left(value) ⇒ Either
Construct a left value of either.
98 99 100 |
# File 'lib/functional/either.rb', line 98 def left(value) new(value, true).freeze end |
.reason ⇒ Either
Construct a left value of either.
101 102 103 |
# File 'lib/functional/either.rb', line 101 def left(value) new(value, true).freeze end |
.right(value) ⇒ Either
Construct a right value of either.
107 108 109 |
# File 'lib/functional/either.rb', line 107 def right(value) new(value, false).freeze end |
.value ⇒ Either
Construct a right value of either.
110 111 112 |
# File 'lib/functional/either.rb', line 110 def right(value) new(value, false).freeze end |
Instance Method Details
#either(lproc, rproc) ⇒ Object
The catamorphism for either. Folds over this either breaking into left or right.
187 188 189 |
# File 'lib/functional/either.rb', line 187 def either(lproc, rproc) left? ? lproc.call(left) : rproc.call(right) end |
#left ⇒ Object Also known as: reason
Projects this either as a left.
140 141 142 |
# File 'lib/functional/either.rb', line 140 def left left? ? to_h[:left] : nil end |
#left? ⇒ Boolean Also known as: reason?, rejected?
Returns true if this either is a left, false otherwise.
156 157 158 |
# File 'lib/functional/either.rb', line 156 def left? @is_left end |
#right ⇒ Object Also known as: value
Projects this either as a right.
148 149 150 |
# File 'lib/functional/either.rb', line 148 def right right? ? to_h[:right] : nil end |
#right? ⇒ Boolean Also known as: value?, fulfilled?
Returns true if this either is a right, false otherwise.
165 166 167 |
# File 'lib/functional/either.rb', line 165 def right? ! left? end |
#swap ⇒ Either
If this is a left, then return the left value in right, or vice versa.
174 175 176 177 178 179 180 |
# File 'lib/functional/either.rb', line 174 def swap if left? self.class.send(:new, left, false) else self.class.send(:new, right, true) end end |