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.


69
70
71
72
# File 'lib/dry/monads/list.rb', line 69

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

Instance Attribute Details

#typeObject (readonly)

Internal array value


66
67
68
# File 'lib/dry/monads/list.rb', line 66

def type
  @type
end

#valueObject (readonly) Also known as: to_ary

Internal array value


66
67
68
# File 'lib/dry/monads/list.rb', line 66

def value
  @value
end

Class Method Details

.[](*values) ⇒ List

Builds a list.

Parameters:

  • values (Array<Object>)

    List elements

Returns:


20
21
22
# File 'lib/dry/monads/list.rb', line 20

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:


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/dry/monads/list.rb', line 29

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:


49
50
51
52
53
54
55
56
57
# File 'lib/dry/monads/list.rb', line 49

def pure(value = Undefined, type = nil, &block)
  if value.equal?(Undefined)
    new([block])
  elsif block
    new([block], value)
  else
    new([value], type)
  end
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:


131
132
133
# File 'lib/dry/monads/list.rb', line 131

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

#apply(list = Undefined) ⇒ List

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

Parameters:

  • list (List) (defaults to: Undefined)

Returns:


292
293
294
295
# File 'lib/dry/monads/list.rb', line 292

def apply(list = Undefined)
  v = Undefined.default(list) { yield }
  fmap(Curry).bind { |f| v.fmap { |x| f.(x) } }
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:


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

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

#empty?TrueClass, FalseClass

Whether the list is empty.

Returns:

  • (TrueClass, FalseClass)

187
188
189
# File 'lib/dry/monads/list.rb', line 187

def empty?
  value.empty?
end

#filterList Also known as: select

Filters elements with a block

Returns:


201
202
203
# File 'lib/dry/monads/list.rb', line 201

def filter
  coerce(value.select { |e| yield(e) })
end

#firstObject

Returns the first element.

Returns:

  • (Object)

154
155
156
# File 'lib/dry/monads/list.rb', line 154

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:


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

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

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

Folds the list from the left.

Parameters:

  • initial (Object)

    Initial value

Returns:

  • (Object)

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

def fold_left(initial)
  value.reduce(initial) { |acc, v| yield(acc, v) }
end

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

Folds the list from the right.

Parameters:

  • initial (Object)

    Initial value

Returns:

  • (Object)

179
180
181
# File 'lib/dry/monads/list.rb', line 179

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>)

223
224
225
# File 'lib/dry/monads/list.rb', line 223

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)

141
142
143
144
# File 'lib/dry/monads/list.rb', line 141

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

#lastObject

Returns the last element.

Returns:

  • (Object)

161
162
163
# File 'lib/dry/monads/list.rb', line 161

def last
  value.last
end

#map(&block) ⇒ List, Enumerator

Maps a block over the list. Acts as Array#map. Note that this method returns an Array instance, not a List

Returns:

  • (List, Enumerator)

116
117
118
119
120
121
122
# File 'lib/dry/monads/list.rb', line 116

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

#monadMonad

Returns the List monad.

Returns:

  • (Monad)

300
301
302
# File 'lib/dry/monads/list.rb', line 300

def monad
  List
end

#reverseList

Reverses the list.

Returns:


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

def reverse
  coerce(value.reverse)
end

#sizeInteger

List size.

Returns:

  • (Integer)

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

def size
  value.size
end

#sortList

Sorts the list.

Returns:


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

def sort
  coerce(value.sort)
end

#tailList

Returns list's tail.

Returns:


230
231
232
# File 'lib/dry/monads/list.rb', line 230

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

#to_monadResult::Success, Result::Failure

Returns self.


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

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


273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/dry/monads/list.rb', line 273

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


239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/dry/monads/list.rb', line 239

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 { |l| l !~ %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)

259
260
261
# File 'lib/dry/monads/list.rb', line 259

def typed?
  !type.nil?
end