Class: Errgonomic::Option::Any

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

Overview

The base class for all options. Some and None are subclasses.

Direct Known Subclasses

None, Some

Instance Method Summary collapse

Instance Method Details

#==(other) ⇒ Object

An option of the same type with an equal inner value is equal.

Because we’re going to monkey patch this into other libraries Rails, we allow some “pass through” functionality into the inner value of a Some, such as comparability here.

TODO: does None == null?

strict:

Some(1) == 1 # => raise Errgonomic::NotComparableError, "Cannot compare Errgonomic::Option::Some with Integer"

Examples:

Some(1) == Some(1) # => true
Some(1) == Some(2) # => false
Some(1) == None() # => false
None() == None() # => true
Some(1) == 1 # => false
None() == nil # => false


26
27
28
29
30
31
# File 'lib/errgonomic/option.rb', line 26

def ==(other)
  return false if self.class != other.class
  return true if none?

  value == other.value
end

#and(other) ⇒ Object

If self is Some, return the provided other Option.

Examples:

None().and(Some(1)) # => None()
Some(2).and(Some(3)) # => Some(3)


258
259
260
261
262
# File 'lib/errgonomic/option.rb', line 258

def and(other)
  return self if none?

  other
end

#and_then(&block) ⇒ Object

If self is Some, call the given block and return its value. Block most return an Option.

Examples:

None().and_then { Some(1) } # => None()
Some(2).and_then { Some(3) } # => Some(3)


269
270
271
272
273
274
275
276
277
278
# File 'lib/errgonomic/option.rb', line 269

def and_then(&block)
  return self if none?

  val = block.call
  if Errgonomic.give_me_ambiguous_downstream_errors? && !val.is_a?(Errgonomic::Option::Any)
    raise Errgonomic::ArgumentError.new, "block must return an Option, was #{val.class.name}"
  end

  val
end

#deconstructObject

Examples:

measurement = Errgonomic::Option::Some.new(1)
case measurement
in Errgonomic::Option::Some, value
  "Measurement is #{measurement.value}"
in Errgonomic::Option::None
  "Measurement is not available"
else
  "not matched"
end # => "Measurement is 1"


43
44
45
46
47
# File 'lib/errgonomic/option.rb', line 43

def deconstruct
  return [self, value] if some?

  [Errgonomic::Option::None]
end

#expect!(msg) ⇒ Object

returns the inner value if pressent, else raises an error with the given message

Examples:

Some(1).expect!("msg") # => 1
None().expect!("msg") # => raise Errgonomic::ExpectError, "msg"

Raises:



102
103
104
105
106
# File 'lib/errgonomic/option.rb', line 102

def expect!(msg)
  raise Errgonomic::ExpectError, msg if none?

  value
end

#map(&block) ⇒ Object

Maps the Option to another Option by applying a function to the contained value (if Some) or returns None. Raises a pedantic exception if the return value of the block is not an Option.

Examples:

Some(1).map { |x| x + 1 } # => Some(2)
None().map { |x| x + 1 } # => None()


153
154
155
156
157
# File 'lib/errgonomic/option.rb', line 153

def map(&block)
  return self if none?

  Some(block.call(value))
end

#map_or(default, &block) ⇒ Object

Returns the provided default (if none), or applies a function to the contained value (if some). If you want lazy evaluation for the provided value, use map_or_else.

Examples:

None().map_or(1) { 100 } # => Some(1)
Some(1).map_or(100) { |x| x + 1 } # => Some(2)
Some("foo").map_or(0) { |str| str.length } # => Some(3)


167
168
169
170
171
# File 'lib/errgonomic/option.rb', line 167

def map_or(default, &block)
  return Some(default) if none?

  Some(block.call(value))
end

#map_or_else(proc, &block) ⇒ Object

Computes a default from the given Proc if None, or applies the block to the contained value (if Some).

Examples:

None().map_or_else(-> { :foo }) { :bar } # => Some(:foo)
Some("str").map_or_else(-> { 100 }) { |str| str.length } # => Some(3)
None().map_or_else( -> { nil }) { |str| str.length } # => None()


180
181
182
183
184
185
186
187
# File 'lib/errgonomic/option.rb', line 180

def map_or_else(proc, &block)
  if none?
    val = proc.call
    return val ? Some(val) : None()
  end

  Some(block.call(value))
end

#none_or(&block) ⇒ Object Also known as: none_or?

return true if the contained value is None or the block returns truthy

Examples:

None().none_or { false } # => true
Some(1).none_or { |x| x > 0 } # => true
Some(1).none_or { |x| x < 0 } # => false


69
70
71
72
73
# File 'lib/errgonomic/option.rb', line 69

def none_or(&block)
  return true if none?

  !!block.call(value)
end

#okObject

convert the option into a result where Some is Ok and None is Err

Examples:

None().ok # => Err()
Some(1).ok # => Ok(1)


193
194
195
196
197
# File 'lib/errgonomic/option.rb', line 193

def ok
  return Errgonomic::Result::Ok.new(value) if some?

  Errgonomic::Result::Err.new
end

#ok_or(err) ⇒ Object

Transforms the option into a result, mapping Some(v) to Ok(v) and None to Err(err)

Examples:

None().ok_or("wow") # => Err("wow")
Some(1).ok_or("such err") # => Ok(1)


204
205
206
207
208
# File 'lib/errgonomic/option.rb', line 204

def ok_or(err)
  return Errgonomic::Result::Ok.new(value) if some?

  Errgonomic::Result::Err.new(err)
end

#ok_or_else(&block) ⇒ Object

Transforms the option into a result, mapping Some(v) to Ok(v) and None to Err(err). TODO: block or proc?

Examples:

None().ok_or_else { "wow" } # => Err("wow")
Some("foo").ok_or_else { "such err" } # => Ok("foo")


216
217
218
219
220
# File 'lib/errgonomic/option.rb', line 216

def ok_or_else(&block)
  return Errgonomic::Result::Ok.new(value) if some?

  Errgonomic::Result::Err.new(block.call)
end

#or(other) ⇒ Object

Returns the option if it contains a value, otherwise returns the provided Option. Returns an Option.

Examples:

None().or(Some(1)) # => Some(1)
Some(2).or(Some(3)) # => Some(2)
None().or(2) # => raise Errgonomic::ArgumentError.new, "other must be an Option, was Integer"

Raises:



228
229
230
231
232
233
234
# File 'lib/errgonomic/option.rb', line 228

def or(other)
  raise ArgumentError, "other must be an Option, was #{other.class.name}" unless other.is_a?(Any)

  return self if some?

  other
end

#or_else(&block) ⇒ Object

Returns the option if it contains a value, otherwise calls the block and returns the result. Returns an Option.

Examples:

None().or_else { Some(1) } # => Some(1)
Some(2).or_else { Some(3) } # => Some(2)
None().or_else { 2 } # => raise Errgonomic::ArgumentError.new, "block must return an Option, was Integer"


242
243
244
245
246
247
248
249
250
251
# File 'lib/errgonomic/option.rb', line 242

def or_else(&block)
  return self if some?

  val = block.call
  if !val.is_a?(Errgonomic::Option::Any) && Errgonomic.give_me_ambiguous_downstream_errors?
    raise Errgonomic::ArgumentError.new, "block must return an Option, was #{val.class.name}"
  end

  val
end

#some_and(&block) ⇒ Object Also known as: some_and?

return true if the contained value is Some and the block returns truthy

Examples:

Some(1).some_and { |x| x > 0 } # => true
Some(0).some_and { |x| x > 0 } # => false
None().some_and { |x| x > 0 } # => false


55
56
57
58
59
# File 'lib/errgonomic/option.rb', line 55

def some_and(&block)
  return false if none?

  !!block.call(value)
end

#tap_some(&block) ⇒ Object

Calls a function with the inner value, if Some, but returns the original option. In Rust, this is “inspect” but that clashes with Ruby conventions. We call this “tap_some” to avoid further clashing with “tap.”

Examples:

tapped = false
Some(1).tap_some { |x| tapped = x } # => Some(1)
tapped # => 1
tapped = false
None().tap_some { tapped = true } # => None()
tapped # => false


141
142
143
144
# File 'lib/errgonomic/option.rb', line 141

def tap_some(&block)
  block.call(value) if some?
  self
end

#to_aObject

return an Array with the contained value, if any

Examples:

Some(1).to_a # => [1]
None().to_a # => []


81
82
83
84
85
# File 'lib/errgonomic/option.rb', line 81

def to_a
  return [] if none?

  [value]
end

#unwrap!Object

returns the inner value if present, else raises an error

Examples:

Some(1).unwrap! # => 1
None().unwrap! # => raise Errgonomic::UnwrapError, "cannot unwrap None"

Raises:



91
92
93
94
95
# File 'lib/errgonomic/option.rb', line 91

def unwrap!
  raise Errgonomic::UnwrapError, 'cannot unwrap None' if none?

  value
end

#unwrap_or(default) ⇒ Object

returns the inner value if present, else returns the default value

Examples:

Some(1).unwrap_or(2) # => 1
None().unwrap_or(2) # => 2


112
113
114
115
116
# File 'lib/errgonomic/option.rb', line 112

def unwrap_or(default)
  return default if none?

  value
end

#unwrap_or_else(&block) ⇒ Object

returns the inner value if present, else returns the result of the provided block

Examples:

Some(1).unwrap_or_else { 2 } # => 1
None().unwrap_or_else { 2 } # => 2


123
124
125
126
127
# File 'lib/errgonomic/option.rb', line 123

def unwrap_or_else(&block)
  return block.call if none?

  value
end

#zip(other) ⇒ Object

Zips self with another Option.

If self is Some(s) and other is Some(o), this method returns Some([s, o]). Otherwise, None is returned.

Examples:

None().zip(Some(1)) # => None()
Some(1).zip(None()) # => None()
Some(2).zip(Some(3)) # => Some([2, 3])


289
290
291
292
293
# File 'lib/errgonomic/option.rb', line 289

def zip(other)
  return None() unless some? && other.some?

  Some([value, other.value])
end

#zip_with(other, &block) ⇒ Object

Zip two options using the block passed. If self is Some and Other is some, yield both of their values to the block and return its value as Some. Else return None.

Examples:

None().zip_with(Some(1)) { |a, b| a + b } # => None()
Some(1).zip_with(None()) { |a, b| a + b } # => None()
Some(2).zip_with(Some(3)) { |a, b| a + b } # => Some(5)


303
304
305
306
307
# File 'lib/errgonomic/option.rb', line 303

def zip_with(other, &block)
  return None() unless some? && other.some?
  other = block.call(value, other.value)
  Some(other)
end