Class: Errgonomic::OptionalArray

Inherits:
Object
  • Object
show all
Includes:
OptionalDig
Defined in:
lib/errgonomic/optional_array.rb

Overview

A companion to Array whose lookups return Options, composed around a plain Array for the same reasons OptionalHash composes around a Hash. Lookups follow element presence: an element holding nil is Some(nil), and only an out-of-bounds index is None, as with Rust's slice get.

Instance Method Summary collapse

Constructor Details

#initialize(array = []) ⇒ OptionalArray

Returns a new instance of OptionalArray.



14
15
16
17
18
19
20
21
# File 'lib/errgonomic/optional_array.rb', line 14

def initialize(array = [])
  unless array.is_a?(::Array)
    raise Errgonomic::TypeMismatchError,
          "OptionalArray wraps an Array, got #{array.class}"
  end

  @array = array
end

Instance Method Details

#==(other) ⇒ Object

Equal to another OptionalArray wrapping an equal array; never equal to a plain Array, mirroring how Some(x) is never equal to x.

Examples:

[1].into_optional == [1].into_optional # => true
[1].into_optional == [2].into_optional # => false
[1].into_optional == [1] # => false


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

def ==(other)
  other.is_a?(OptionalArray) && inner == other.inner
end

#[](index) ⇒ Object

Retrieve the element at an integer index, wrapped in an Option. Negative indexes count from the end, as usual. A non-integer index raises, pedantically: the silent nil of Array#[] with a bad argument is the ambiguity this class exists to remove.

Examples:

a = [:a, nil].into_optional
a[0] # => Some(:a)
a[1] # => Some(nil)
a[2] # => None()
a[-1] # => Some(nil)
a[:nope] # => raise Errgonomic::TypeMismatchError, "index must be an Integer, got Symbol"


35
36
37
38
39
40
41
42
43
# File 'lib/errgonomic/optional_array.rb', line 35

def [](index)
  unless index.is_a?(::Integer)
    raise Errgonomic::TypeMismatchError,
          "index must be an Integer, got #{index.class}"
  end
  return None() unless (-@array.length...@array.length).cover?(index)

  Some(@array[index])
end

#[]=(index, value) ⇒ Object

Write through to the underlying array.

Examples:

a = [].into_optional
a[0] = :a
a[0] # => Some(:a)


51
52
53
# File 'lib/errgonomic/optional_array.rb', line 51

def []=(index, value)
  @array[index] = value
end

#dig(index, *rest) ⇒ Object

Like Array#dig, but every step checks presence, so an absent path (None) stays distinct from a present nil (Some(nil)).

Examples:

a = [{ name: 'Ada' }].into_optional
a.dig(0, :name) # => Some("Ada")
a.dig(0, :nickname) # => None()
a.dig(1, :name) # => None()


63
64
65
# File 'lib/errgonomic/optional_array.rb', line 63

def dig(index, *rest)
  optional_dig(@array, [index, *rest])
end

#empty?Boolean

Examples:

[].into_optional.empty? # => true
[1].into_optional.empty? # => false

Returns:

  • (Boolean)


93
94
95
# File 'lib/errgonomic/optional_array.rb', line 93

def empty?
  @array.empty?
end

#eql?(other) ⇒ Boolean

Examples:

[1].into_optional.eql?([1].into_optional) # => true
{ [1].into_optional => :hit }[[1].into_optional] # => :hit

Returns:

  • (Boolean)


126
127
128
# File 'lib/errgonomic/optional_array.rb', line 126

def eql?(other)
  other.is_a?(OptionalArray) && inner.eql?(other.inner)
end

#firstObject

The first element as an Option, as with Rust's slice first.

Examples:

[1, 2].into_optional.first # => Some(1)
[nil].into_optional.first # => Some(nil)
[].into_optional.first # => None()


73
74
75
76
77
# File 'lib/errgonomic/optional_array.rb', line 73

def first
  return None() if @array.empty?

  Some(@array.first)
end

#hashObject



130
131
132
# File 'lib/errgonomic/optional_array.rb', line 130

def hash
  [self.class, inner].hash
end

#inspectObject

Examples:

[1].into_optional.inspect # => "OptionalArray([1])"


136
137
138
# File 'lib/errgonomic/optional_array.rb', line 136

def inspect
  "OptionalArray(#{@array.inspect})"
end

#lastObject

The last element as an Option, as with Rust's slice last.

Examples:

[1, 2].into_optional.last # => Some(2)
[].into_optional.last # => None()


84
85
86
87
88
# File 'lib/errgonomic/optional_array.rb', line 84

def last
  return None() if @array.empty?

  Some(@array.last)
end

#sizeObject

Examples:

[1, 2].into_optional.size # => 2


99
100
101
# File 'lib/errgonomic/optional_array.rb', line 99

def size
  @array.size
end

#to_aObject

The escape hatch back to a plain Array: a shallow copy, so array-shaped code cannot mutate the wrapped state behind the Option semantics.

Examples:

[1].into_optional.to_a # => [1]


108
109
110
# File 'lib/errgonomic/optional_array.rb', line 108

def to_a
  @array.dup
end