Class: Dry::Monads::List

Inherits:
Object
  • Object
show all
Includes:
Transformer
Defined in:
lib/dry/monads/list.rb

Overview

The List monad.

Defined Under Namespace

Modules: Mixin Classes: ListBuilder

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Transformer

#fmap2, #fmap3

Constructor Details

#initialize(value, type = nil) ⇒ List

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of List.



85
86
87
88
# File 'lib/dry/monads/list.rb', line 85

def initialize(value, type = nil)
  @value = value
  @type = type
end

Instance Attribute Details

#typeObject (readonly)

Internal array value



82
83
84
# File 'lib/dry/monads/list.rb', line 82

def type
  @type
end

#valueObject (readonly) Also known as: to_ary

Internal array value



82
83
84
# File 'lib/dry/monads/list.rb', line 82

def value
  @value
end

Class Method Details

.[](*values) ⇒ List

Builds a list.

Parameters:

  • values (Array<Object>)

    List elements

Returns:



12
13
14
# File 'lib/dry/monads/list.rb', line 12

def [](*values)
  new(values)
end

.coerce(value, type = nil) ⇒ List

Coerces a value to a list. ‘nil` will be coerced to an empty list.

Parameters:

  • value (Object)

    Value

  • type (Monad) (defaults to: nil)

    Embedded monad type (used in case of list of monadic values)

Returns:



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/dry/monads/list.rb', line 21

def coerce(value, type = nil)
  if value.nil?
    List.new([], type)
  elsif value.respond_to?(:to_ary)
    values = value.to_ary

    if !values.empty? && type.nil? && values[0].respond_to?(:monad)
      List.new(values, values[0].monad)
    else
      List.new(values, type)
    end
  else
    raise TypeError, "Can't coerce #{value.inspect} to List"
  end
end

.pure(value = Undefined, type = nil, &block) ⇒ List

Wraps a value with a list.

Parameters:

  • value (Object) (defaults to: Undefined)

    any object

Returns:



41
42
43
44
45
46
47
48
49
# File 'lib/dry/monads/list.rb', line 41

def pure(value = Undefined, type = nil, &block)
  if value.equal?(Undefined)
    new([block])
  elsif block
    new([block], value)
  else
    new([value], type)
  end
end

.unfold(state, type = nil) ⇒ List

Iteratively builds a new list from a block returning Maybe values

Parameters:

  • state (Object.new)

    Initial state

  • type (#pure) (defaults to: nil)

    Type of list element

Returns:

See Also:



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/dry/monads/list.rb', line 58

def unfold(state, type = nil)
  xs = []

  loop do
    m = yield(state)

    if m.some?
      state, x = m.value!
      xs << x
    else
      break
    end
  end

  new(xs, type)
end

Instance Method Details

#+(other) ⇒ List

Concatenates two lists.

Examples:

Dry::Monads::List[1, 2] + Dry::Monads::List[3, 4] # => List[1, 2, 3, 4]

Parameters:

  • other (List)

    Other list

Returns:



147
148
149
# File 'lib/dry/monads/list.rb', line 147

def +(other)
  List.new(to_ary + other.to_ary)
end

#apply(list = Undefined, &block) ⇒ List

Applies the stored functions to the elements of the given list.

Parameters:

  • list (List) (defaults to: Undefined)

Returns:



308
309
310
311
# File 'lib/dry/monads/list.rb', line 308

def apply(list = Undefined, &block)
  v = Undefined.default(list, &block)
  fmap(Curry).bind { |f| v.fmap { f.(_1) } }
end

#bind(*args) ⇒ List

Lifts a block/proc and runs it against each member of the list. The block must return a value coercible to a list. As in other monads if no block given the first argument will be treated as callable and used instead.

Examples:

Dry::Monads::List[1, 2].bind { |x| [x + 1] } # => List[2, 3]
Dry::Monads::List[1, 2].bind(-> x { [x, x + 1] }) # => List[1, 2, 2, 3]

Parameters:

  • args (Array<Object>)

    arguments will be passed to the block or proc

Returns:



101
102
103
104
105
106
107
108
# File 'lib/dry/monads/list.rb', line 101

def bind(*args)
  if block_given?
    List.coerce(value.map { yield(_1, *args) }.reduce([], &:+))
  else
    obj, *rest = args
    List.coerce(value.map { obj.(_1, *rest) }.reduce([], &:+))
  end
end

#collectList

Iterates over the list and collects Some values.

Examples:

with block syntax

n = 20
List[10, 5, 0].collect do |divisor|
  if divisor.zero?
    None()
  else
    Some(n / divisor)
  end
end
# => List[2, 4]

without block

List[Some(5), None(), Some(3)].collect.map { |x| x * 2 }
# => [10, 6]

Returns:



345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/dry/monads/list.rb', line 345

def collect
  if block_given?
    collected = value.each_with_object([]) do |x, ys|
      y = yield(x)
      ys << y.value! if y.some?
    end

    List.new(collected)
  else
    Enumerator.new do |g|
      value.each { g << _1.value! if _1.some? }
    end
  end
end

#deconstructObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Pattern matching

Examples:

case List[1, 2, 3]
in List[1, 2, x] then ...
in List[Integer, _, _] then ...
in List[0..2, _, _] then ...
end


370
371
372
# File 'lib/dry/monads/list.rb', line 370

def deconstruct
  value
end

#empty?TrueClass, FalseClass

Whether the list is empty.

Returns:

  • (TrueClass, FalseClass)


203
204
205
# File 'lib/dry/monads/list.rb', line 203

def empty?
  value.empty?
end

#filter(&block) ⇒ List Also known as: select

Filters elements with a block

Returns:



217
218
219
# File 'lib/dry/monads/list.rb', line 217

def filter(&block)
  coerce(value.select(&block))
end

#firstObject

Returns the first element.

Returns:

  • (Object)


170
171
172
# File 'lib/dry/monads/list.rb', line 170

def first
  value.first
end

#fmap(*args) ⇒ List

Maps a block over the list. Acts as ‘Array#map`. As in other monads if no block given the first argument will be treated as callable and used instead.

Examples:

Dry::Monads::List[1, 2].fmap { |x| x + 1 } # => List[2, 3]

Parameters:

  • args (Array<Object>)

    arguments will be passed to the block or proc

Returns:



119
120
121
122
123
124
125
126
# File 'lib/dry/monads/list.rb', line 119

def fmap(*args)
  if block_given?
    List.new(value.map { yield(_1, *args) })
  else
    obj, *rest = args
    List.new(value.map { obj.(_1, *rest) })
  end
end

#fold_left(initial, &block) ⇒ Object Also known as: foldl, reduce

Folds the list from the left.

Parameters:

  • initial (Object)

    Initial value

Returns:

  • (Object)


185
186
187
# File 'lib/dry/monads/list.rb', line 185

def fold_left(initial, &block)
  value.reduce(initial, &block)
end

#fold_right(initial) ⇒ Object Also known as: foldr

Folds the list from the right.

Parameters:

  • initial (Object)

    Initial value

Returns:

  • (Object)


195
196
197
# File 'lib/dry/monads/list.rb', line 195

def fold_right(initial)
  value.reverse.reduce(initial) { |a, b| yield(b, a) }
end

#headMaybe<Object>

Returns the first element wrapped with a ‘Maybe`.

Returns:

  • (Maybe<Object>)


239
240
241
# File 'lib/dry/monads/list.rb', line 239

def head
  Monads::Maybe.coerce(value.first)
end

#inspectString Also known as: to_s

Returns a string representation of the list.

Examples:

Dry::Monads::List[1, 2, 3].inspect # => "List[1, 2, 3]"

Returns:

  • (String)


157
158
159
160
# File 'lib/dry/monads/list.rb', line 157

def inspect
  type_ann = typed? ? "<#{type.name.split("::").last}>" : ""
  "List#{type_ann}#{value.inspect}"
end

#lastObject

Returns the last element.

Returns:

  • (Object)


177
178
179
# File 'lib/dry/monads/list.rb', line 177

def last
  value.last
end

#map(&block) ⇒ List, Enumerator

Maps a block over the list. Acts as ‘Array#map`. If called without a block, this method returns an enumerator, not a List

Returns:

  • (List, Enumerator)


132
133
134
135
136
137
138
# File 'lib/dry/monads/list.rb', line 132

def map(&block)
  if block_given?
    fmap(block)
  else
    value.map
  end
end

#monadMonad

Returns the List monad.

Returns:

  • (Monad)


316
317
318
# File 'lib/dry/monads/list.rb', line 316

def monad
  List
end

#reverseList

Reverses the list.

Returns:



232
233
234
# File 'lib/dry/monads/list.rb', line 232

def reverse
  coerce(value.reverse)
end

#sizeInteger

List size.

Returns:

  • (Integer)


225
226
227
# File 'lib/dry/monads/list.rb', line 225

def size
  value.size
end

#sortList

Sorts the list.

Returns:



210
211
212
# File 'lib/dry/monads/list.rb', line 210

def sort
  coerce(value.sort)
end

#tailList

Returns list’s tail.

Returns:



246
247
248
# File 'lib/dry/monads/list.rb', line 246

def tail
  coerce(value.drop(1))
end

#to_monadList

Returns self.

Returns:



323
324
325
# File 'lib/dry/monads/list.rb', line 323

def to_monad
  self
end

#traverse(proc = nil, &block) ⇒ Monad

Traverses the list with a block (or without it). This methods “flips” List structure with the given monad (obtained from the type). Note that traversing requires the list to be typed. Also if a block given, its returning type must be equal list’s type.

Examples:

List<Result>[Success(1), Success(2)].traverse # => Success([1, 2])
List<Maybe>[Some(1), None, Some(3)].traverse # => None

Returns:

  • (Monad)

    Result is a monadic value



289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/dry/monads/list.rb', line 289

def traverse(proc = nil, &block)
  unless typed?
    raise StandardError, "Cannot traverse an untyped list"
  end

  cons = type.pure { |list, i| list + List.pure(i) }
  with = proc || block || Traverse[type]

  foldl(type.pure(EMPTY)) do |acc, el|
    cons
      .apply(acc)
      .apply { with.(el) }
  end
end

#typed(type = nil) ⇒ List

Turns the list into a typed one. Type is required for some operations like .traverse.

Parameters:

  • type (Monad) (defaults to: nil)

    Monad instance

Returns:

  • (List)

    Typed list



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/dry/monads/list.rb', line 255

def typed(type = nil)
  if type.nil?
    if size.zero?
      raise ArgumentError, "Cannot infer a monad for an empty list"
    else
      self.class.warn(
        "Automatic monad inference is deprecated, pass a type explicitly "\
        "or use a predefined constant, e.g. List::Result\n"\
        "#{caller.find { _1 !~ %r{(lib/dry/monads)|(gems)} }}"
      )
      self.class.new(value, value[0].monad)
    end
  else
    self.class.new(value, type)
  end
end

#typed?Boolean

Whether the list is types

Returns:

  • (Boolean)


275
276
277
# File 'lib/dry/monads/list.rb', line 275

def typed?
  !type.nil?
end