Class: Plumb::Result

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

Overview

The value + validity + errors triple that flows through a composition.

There are two ways to derive one result from another:

- #valid / #invalid ALLOCATE a fresh Result and leave the receiver
untouched. This is the safe, functional form: use it in custom types, and
anywhere a result may outlive the call or be captured (Streams close over
it lazily; concurrent Arrays hand results between threads). This is the
public API for user-defined steps.

- #valid! / #invalid! FLIP THE RECEIVER in place and return it (no
allocation). Plumb's own built-in steps use these on the hot path to
avoid a Result per node — but only where the input cursor is theirs to
mutate: never on a shared/constant Result, never when the result escapes
into a closure or another thread. The one fork point (Or) snapshots the
value and #resets before retrying its second branch.

So built-ins stay allocation-lean while user code (and the concurrency- and laziness-sensitive built-ins) keep the footgun-free copying semantics, at the cost of a few extra allocations there.

Valid and Invalid were once separate subclasses; collapsing them into one class with a boolean is what makes the in-place valid<->invalid flip possible (Ruby can't change an object's class).

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value, valid: true, errors: nil) ⇒ Result

Returns a new instance of Result.



47
48
49
50
51
# File 'lib/plumb/result.rb', line 47

def initialize(value, valid: true, errors: nil)
  @value = value
  @valid = valid
  @errors = errors
end

Instance Attribute Details

#errorsObject (readonly)

Returns the value of attribute errors.



45
46
47
# File 'lib/plumb/result.rb', line 45

def errors
  @errors
end

#valueObject (readonly)

Returns the value of attribute value.



45
46
47
# File 'lib/plumb/result.rb', line 45

def value
  @value
end

Class Method Details

.invalid(value = nil, errors: nil) ⇒ Object



34
35
36
# File 'lib/plumb/result.rb', line 34

def invalid(value = nil, errors: nil)
  new(value, valid: false, errors:)
end

.valid(value = nil) ⇒ Object



30
31
32
# File 'lib/plumb/result.rb', line 30

def valid(value = nil)
  new(value, valid: true)
end

.wrap(value) ⇒ Object



38
39
40
41
42
# File 'lib/plumb/result.rb', line 38

def wrap(value)
  return value if value.is_a?(Result)

  valid(value)
end

Instance Method Details

#inspectObject



56
57
58
# File 'lib/plumb/result.rb', line 56

def inspect
  %(<#{self.class}##{object_id} valid:#{@valid} value:#{value.inspect} errors:#{errors.inspect}>)
end

#invalid(val = value, errors: nil) ⇒ Object



65
66
67
# File 'lib/plumb/result.rb', line 65

def invalid(val = value, errors: nil)
  Result.invalid(val, errors:)
end

#invalid!(val = value, errors: nil) ⇒ Object



78
79
80
81
82
83
# File 'lib/plumb/result.rb', line 78

def invalid!(val = value, errors: nil)
  @value = val
  @valid = false
  @errors = errors
  self
end

#invalid?Boolean

Returns:

  • (Boolean)


54
# File 'lib/plumb/result.rb', line 54

def invalid? = !@valid

#map(callable) ⇒ Object



92
93
94
# File 'lib/plumb/result.rb', line 92

def map(callable)
  @valid ? callable.call(self) : self
end

#reset(val) ⇒ Object

Reset the cursor to a fresh VALID state carrying val, in place. The mutating scratch-reuse primitive behind the HashClass/ArrayClass element loops (same as #valid!(val)).



88
89
90
# File 'lib/plumb/result.rb', line 88

def reset(val)
  valid!(val)
end

#valid(val = value) ⇒ Object

Allocating, copy-returning form. Safe everywhere: the receiver is untouched.



61
62
63
# File 'lib/plumb/result.rb', line 61

def valid(val = value)
  Result.valid(val)
end

#valid!(val = value) ⇒ Object

In-place form: flip THIS result and return self. No allocation. Only for code that owns the cursor (see the class docstring).



71
72
73
74
75
76
# File 'lib/plumb/result.rb', line 71

def valid!(val = value)
  @value = val
  @valid = true
  @errors = nil
  self
end

#valid?Boolean

Returns:

  • (Boolean)


53
# File 'lib/plumb/result.rb', line 53

def valid? = @valid