class Dry::Monads::List

The List monad.

Constants

EMPTY

Empty list

Maybe

List of maybes

Result

List of results

Task

List of tasks

Try

List of tries

Validated

List of validation results

Attributes

to_a[R]

Internal array value

to_ary[R]

Internal array value

type[R]

Internal array value

value[R]

Internal array value

Private Class Methods

[](*values) click to toggle source

Builds a list.

@param values [Array<Object>] List elements @return [List]

# File lib/dry/monads/list.rb, line 12
def [](*values)
  new(values)
end
coerce(value, type = nil) click to toggle source

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

@param value [Object] Value @param type [Monad] Embedded monad type (used in case of list of monadic values) @return [List]

# 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
new(value, type = nil) click to toggle source

@api private

# File lib/dry/monads/list.rb, line 85
def initialize(value, type = nil)
  @value = value
  @type = type
end
pure(value = Undefined, type = nil, &block) click to toggle source

Wraps a value with a list.

@param value [Object] any object @return [List]

# 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) { |state| ... } click to toggle source

Iteratively builds a new list from a block returning Maybe values

@see hackage.haskell.org/package/base-4.12.0.0/docs/Data-List.html#g:9

@param state [Object.new] Initial state @param type [#pure] Type of list element @return [List]

# 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

Private Instance Methods

+(other) click to toggle source

Concatenates two lists.

@example

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

@param other [List] Other list @return [List]

# File lib/dry/monads/list.rb, line 147
def +(other)
  List.new(to_ary + other.to_ary)
end
apply(list = Undefined, &block) click to toggle source

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

@param list [List] @return [List]

# 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) { |_1, *args| ... } click to toggle source

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.

@example

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]

@param args [Array<Object>] arguments will be passed to the block or proc @return [List]

# 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
coerce(other) click to toggle source
# File lib/dry/monads/list.rb, line 376
def coerce(other)
  self.class.coerce(other)
end
collect() { |x| ... } click to toggle source

Iterates over the list and collects Some values.

@example 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]

@example without block

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

@return [List]

# 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
deconstruct() click to toggle source

Pattern matching

@example

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

@api private

# File lib/dry/monads/list.rb, line 370
def deconstruct
  value
end
empty?() click to toggle source

Whether the list is empty.

@return [TrueClass, FalseClass]

# File lib/dry/monads/list.rb, line 203
def empty?
  value.empty?
end
filter(&block) click to toggle source

Filters elements with a block

@return [List]

# File lib/dry/monads/list.rb, line 217
def filter(&block)
  coerce(value.select(&block))
end
Also aliased as: select
first() click to toggle source

Returns the first element.

@return [Object]

# File lib/dry/monads/list.rb, line 170
def first
  value.first
end
fmap(*args) { |_1, *args| ... } click to toggle source

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.

@example

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

@param args [Array<Object>] arguments will be passed to the block or proc @return [List]

# 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) click to toggle source

Folds the list from the left.

@param initial [Object] Initial value @return [Object]

# File lib/dry/monads/list.rb, line 185
def fold_left(initial, &block)
  value.reduce(initial, &block)
end
Also aliased as: foldl, reduce
fold_right(initial) { |b, a| ... } click to toggle source

Folds the list from the right.

@param initial [Object] Initial value @return [Object]

# File lib/dry/monads/list.rb, line 195
def fold_right(initial)
  value.reverse.reduce(initial) { |a, b| yield(b, a) }
end
Also aliased as: foldr
foldl(initial, &block)
Alias for: fold_left
foldr(initial)
Alias for: fold_right
head() click to toggle source

Returns the first element wrapped with a ‘Maybe`.

@return [Maybe<Object>]

# File lib/dry/monads/list.rb, line 239
def head
  Monads::Maybe.coerce(value.first)
end
inspect() click to toggle source

Returns a string representation of the list.

@example

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

@return [String]

# File lib/dry/monads/list.rb, line 157
def inspect
  type_ann = typed? ? "<#{type.name.split("::").last}>" : ""
  "List#{type_ann}#{value.inspect}"
end
Also aliased as: to_s
last() click to toggle source

Returns the last element.

@return [Object]

# File lib/dry/monads/list.rb, line 177
def last
  value.last
end
map(&block) click to toggle source

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

@return [List,Enumerator]

# File lib/dry/monads/list.rb, line 132
def map(&block)
  if block_given?
    fmap(block)
  else
    value.map
  end
end
monad() click to toggle source

Returns the List monad.

@return [Monad]

# File lib/dry/monads/list.rb, line 316
def monad
  List
end
reduce(initial, &block)
Alias for: fold_left
reverse() click to toggle source

Reverses the list.

@return [List]

# File lib/dry/monads/list.rb, line 232
def reverse
  coerce(value.reverse)
end
select(&block)
Alias for: filter
size() click to toggle source

List size.

@return [Integer]

# File lib/dry/monads/list.rb, line 225
def size
  value.size
end
sort() click to toggle source

Sorts the list.

@return [List]

# File lib/dry/monads/list.rb, line 210
def sort
  coerce(value.sort)
end
tail() click to toggle source

Returns list’s tail.

@return [List]

# File lib/dry/monads/list.rb, line 246
def tail
  coerce(value.drop(1))
end
to_monad() click to toggle source

Returns self.

@return [List]

# File lib/dry/monads/list.rb, line 323
def to_monad
  self
end
to_s()
Alias for: inspect
traverse(proc = nil, &block) click to toggle source

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.

@example

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

@return [Monad] Result is a monadic value

# 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) click to toggle source

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

@param type [Monad] Monad instance @return [List] Typed list

# 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?() click to toggle source

Whether the list is types

@return [Boolean]

# File lib/dry/monads/list.rb, line 275
def typed?
  !type.nil?
end