Module: Either

Includes:
Comparable, Control::Monad
Included in:
Left, Right
Defined in:
lib/data.either.rb

Overview

The ‘Either` union type represents values with two possibilities:

‘Either a b` is either `Left a` or `Right b`

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Control::Monad

#>>

Methods included from Helper

#alias_names

Class Method Details

.lefts(list_of_either) ⇒ value

filter only Left value from List of Either

“‘ ruby Either.lefts [Left.new(1),Right.new(5), Right.new(2)] # => [1] “`

Returns:

  • (value)


210
211
212
213
# File 'lib/data.either.rb', line 210

def lefts list_of_either
  list_of_either.select(&:left?)
    .map { |left| left.when({Left: ->l{l}}) }
end

.partition(list_of_either) ⇒ [l], [r]

partion a List of Either into a List of 2 List, one contains only Left, other contains only Right

“‘ ruby Either.partition [Left.new(1),Right.new(5), Right.new(2)] # => [[1],[5, 2]] “`

Returns:

  • ([l], [r])


221
222
223
224
225
226
227
228
229
# File 'lib/data.either.rb', line 221

def partition list_of_either
  list_of_either.inject([[],[]]) do |acc, x|
    x.when({
             Left: ->(l){acc[0].push(l)},
             Right: ->(r){acc[1].push(r)}
           })
    acc
  end
end

.rights(list_of_either) ⇒ value

filter only Right value from List of Either

“‘ ruby Either.rights [Left.new(1),Right.new(5), Right.new(2)] # => [5, 2] “`

Returns:

  • (value)


199
200
201
202
# File 'lib/data.either.rb', line 199

def rights list_of_either
  list_of_either.select(&:right?)
    .map { |right| right.get_or_else(nil) }
end

Instance Method Details

#<=>(other) ⇒ Object

comparable

  • Left < Right



171
172
173
174
175
176
177
178
179
180
# File 'lib/data.either.rb', line 171

def <=> other
  case self
  when Right
    other =~ {Right: ->o{ @v <=> o},
              Left: ->_{1}}
  else
    other =~ {Right: ->_{ -1 },
              Left: ->o{ @v <=> o}}
  end
end

#bimap(lfn, rfn) ⇒ Either

‘bimap` accept 2 lambdas, if it’s [Right], apply the 2nd lambda, otherwise apply to the first lambda

“‘ ruby

Right.new(1).bimap ->(x){x-1}, ->(x){x+1} # => 2
Left.new(1).bimap ->(x){x-1}, ->(x){x+1}) # => 0

“‘

Returns:



90
91
92
93
94
95
96
97
# File 'lib/data.either.rb', line 90

def bimap lfn, rfn
  case self
    when Right
      Right.new(rfn.(@v))
    else
      Left.new(lfn.(@v))
  end
end

#each(&block) ⇒ Object



156
157
158
# File 'lib/data.either.rb', line 156

def each &block
  bimap(->_{}, &blcok)
end

#flat_mapEither

it override Monad#flat_map, as Haskell’s ‘>flat_map` method if it’s Right, pass the value to #flat_map’s block, and flat the result of the block.

when it’s Left, do nothing “‘ ruby expect(Right.new(1).flat_map { |x| Left.new } ).to eq(Left.new) expect(Left.new(1).flat_map { |x| Left.new } ).to eq(Left.new(1)) “`

Returns:



109
110
111
112
113
114
115
116
# File 'lib/data.either.rb', line 109

def flat_map
  case self
  when Right
    yield @v
  else
    self
  end
end

#get_or_else(e) ⇒ Object Also known as: |

get value ‘a` out from `Right a`, otherwise return `e` “`ruby Right.new(1).get_or_else(2) # => 1 Right.new(1) | 2 # => 1 “`



31
32
33
34
35
36
37
38
# File 'lib/data.either.rb', line 31

def get_or_else e
  case self
    when Right
      @v
    else
      e
  end
end

#initialize(v) ⇒ Either

Either only contain one value @v

Returns:



12
13
14
# File 'lib/data.either.rb', line 12

def initialize v
  @v = v
end

#inspectString

Returns:

  • (String)


183
184
185
186
187
188
189
190
# File 'lib/data.either.rb', line 183

def inspect
  case self
  when Left
    "#<Left value=#{@v}>"
  else
    "#<Right value=#{@v}>"
  end
end

#left?Boolean

default ‘false`, should override in Left or Right

Returns:

  • (Boolean)


18
19
20
# File 'lib/data.either.rb', line 18

def left?
  false
end

#left_mapEither

the opposit of #map, apply function to ‘Left e`, do nothing if it’s ‘Right a`

“‘ ruby

Right.new(1).left_map {|x| x + 1} # => #<Right value=1>
Left.new(1).left_map {|x| x + 1} # => #<Left value=2>

“‘

Returns:



74
75
76
77
78
79
80
81
# File 'lib/data.either.rb', line 74

def left_map
  case self
  when Left
    Left.new(yield @v)
  else
    self
  end
end

#mapEither

overide of Functor’s ‘fmap`, apply function on `Right a`’s value ‘a`, do nothing if it’s ‘Left e`

“‘ ruby

Right.new(1).map {|x| x + 1} # => #<Right value=2>
Left.new(1).map {|x| x + 1} # => #<Left value=1>

“‘

Returns:



59
60
61
62
63
64
65
66
# File 'lib/data.either.rb', line 59

def map
  case self
  when Right
    Right.new(yield @v)
  else
    self
  end
end

#or_else(e) ⇒ Object



42
43
44
45
46
47
48
49
# File 'lib/data.either.rb', line 42

def or_else e
  case self
    when Right
      
    else
      e
  end
end

#right?Boolean

default ‘false`, should override in Left or Right

Returns:

  • (Boolean)


22
23
24
# File 'lib/data.either.rb', line 22

def right?
  false
end

#to_aObject



160
161
162
163
164
165
166
167
# File 'lib/data.either.rb', line 160

def to_a
  case self
  when Right
    [@v]
  else
    []
  end
end

#when(what) ⇒ Either Also known as: match, =~

similar to Scala’s ‘match` for case class

will pattern match the value out and pass to matched lambda “‘ruby Right.new(1).when(->x{x+1 }) # => 2 Right.new(1).when(->x{x+1) # => nil Right.new(1) =~ (->x{x+1, _: ->xx-1 }) # => 0 “`

Returns:



127
128
129
130
131
132
133
134
# File 'lib/data.either.rb', line 127

def when what
  current_class = self.class.to_s.to_sym
  if what.include? current_class
    what[current_class].(@v)
  elsif what.include? :_
    what[:_].(@v)
  end
end

#~@Either Also known as: swap

swap type of [Either]

“‘ruby

~Right.new(1) # => Left.new(1)
~Left.new(2) # => Right.new(2)

“‘

Returns:



146
147
148
149
150
151
152
153
# File 'lib/data.either.rb', line 146

def ~@
  case self
  when Right
    Left.new @v
  else
    Right.new @v
  end
end