module Rumonade::Monad

Mix-in for common monad functionality dependent on implementation of monadic methods unit and bind

Notes:

Constants

DEFAULT_METHODS_TO_REPLACE_WITH_MONAD

Methods to replace when mixed in – unless class defines METHODS_TO_REPLACE_WITH_MONAD

Public Class Methods

included(base) click to toggle source

When mixed into a class, this callback is executed

# File lib/rumonade/monad.rb, line 15
def self.included(base)
  methods_to_replace = base::METHODS_TO_REPLACE_WITH_MONAD rescue DEFAULT_METHODS_TO_REPLACE_WITH_MONAD

  base.class_eval do
    # optimization: replace flat_map with an alias for bind, as they are identical
    alias_method :flat_map_with_monad, :bind

    methods_to_replace.each do |method_name|
      alias_method "#{method_name}_without_monad".to_sym, method_name if public_instance_methods.include? method_name
      alias_method method_name, "#{method_name}_with_monad".to_sym
    end
  end
end

Public Instance Methods

can_flatten_in_monad?() click to toggle source

Returns true if flatten_with_monad can call recursively on contained values members (eg. elements of an array).

NOTE: Is overridden in Hash.

# File lib/rumonade/monad.rb, line 99
def can_flatten_in_monad?
  true
end
each(lam = nil, &blk) click to toggle source

Applies the given procedure to each element in this monad

# File lib/rumonade/monad.rb, line 32
def each(lam = nil, &blk)
  bind { |v| (lam || blk).call(v) }; nil
end
find_all(lam = nil, &blk)
Alias for: select
flat_map_with_monad(lam = nil, &blk) click to toggle source

Returns the results of applying the given function to each element in this monad

NOTE: normally aliased as flat_map when Monad is mixed into a class

# File lib/rumonade/monad.rb, line 46
def flat_map_with_monad(lam = nil, &blk)
  bind(lam || blk)
end
flatten_with_monad(depth=nil) click to toggle source

Returns a monad whose elements are the ultimate (non-monadic) values contained in all nested monads

NOTE: normally aliased as flatten when Monad is mixed into a class

@example

[Some(Some(1)), Some(Some(None))], [None]].flatten_with_monad
#=> [1]
# File lib/rumonade/monad.rb, line 58
def flatten_with_monad(depth=nil)
  if depth.is_a? Integer
    depth.times.inject(self) { |e, _| e.shallow_flatten }
  else
    bind do |x|
      if x.is_a?(Monad) && x.can_flatten_in_monad?
        x.flatten_with_monad
      else
        self.class.unit(x)
      end
    end
  end
end
map_with_monad(lam = nil, &blk) click to toggle source

Returns a monad whose elements are the results of applying the given function to each element in this monad

NOTE: normally aliased as map when Monad is mixed into a class

# File lib/rumonade/monad.rb, line 39
def map_with_monad(lam = nil, &blk)
  bind { |v| self.class.unit((lam || blk).call(v)) }
end
select(lam = nil, &blk) click to toggle source

Returns a monad whose elements are all those elements of this monad for which the given predicate returned true

# File lib/rumonade/monad.rb, line 73
def select(lam = nil, &blk)
  bind { |x| (lam || blk).call(x) ? self.class.unit(x) : self.class.empty }
end
Also aliased as: find_all
shallow_flatten() click to toggle source

Returns a monad whose elements are the values contained in the first level of nested monads

This method is equivalent to the Scala flatten call (single-level flattening), whereas flatten is in keeping with the native Ruby flatten calls (multiple-level flattening).

@example

[Some(Some(1)), Some(Some(None)), [None]].shallow_flatten
#=> [Some(1), Some(None), None]
[Some(1), Some(None), None].shallow_flatten
#=> [1, None]
[1, None].shallow_flatten
#=> [1]
# File lib/rumonade/monad.rb, line 91
def shallow_flatten
  bind { |x| x.is_a?(Monad) ? x : self.class.unit(x) }
end