lib/rumonade/monad.rb in rumonade-0.1.0 vs lib/rumonade/monad.rb in rumonade-0.1.1
- old
+ new
@@ -1,12 +1,17 @@
module Rumonade
- # TODO: Document this
+ # Mix-in for common monad functionality dependent on implementation of monadic methods unit and bind
+ #
+ # Notes:
+ # * classes should include this module AFTER defining the monadic methods unit and bind
+ #
module Monad
- METHODS_TO_REPLACE = [:flat_map, :flatten]
+ METHODS_TO_REPLACE = [:flat_map, :flatten] # :nodoc:
- def self.included(base)
+ def self.included(base) # :nodoc:
base.class_eval do
+ # optimization: replace flat_map with an alias for bind, as they are identical
alias_method :flat_map_with_monad, :bind
# force only a few methods to be aliased to monad versions; others can stay with native or Enumerable versions
METHODS_TO_REPLACE.each do |method_name|
alias_method "#{method_name}_without_monad".to_sym, method_name if public_instance_methods.include? method_name
@@ -15,22 +20,45 @@
end
end
include Enumerable
- def map(lam = nil, &blk)
- bind { |v| (lam || blk).call(v) }
+ # Applies the given procedure to each element in this monad
+ def each(lam = nil, &blk)
+ bind { |v| (lam || blk).call(v) }; nil
end
- def each(lam = nil, &blk)
- map(lam || blk); nil
+ # Returns a monad whose elements are the results of applying the given function to each element in this monad
+ def map(lam = nil, &blk)
+ bind { |v| self.class.unit((lam || blk).call(v)) }
end
- def shallow_flatten
- bind { |x| x.is_a?(Monad) ? x : self.class.unit(x) }
+ # Returns the results of applying the given function to each element in this monad
+ def flat_map_with_monad(lam = nil, &blk)
+ bind(lam || blk)
end
+ # Returns a monad whose elements are the ultimate (non-monadic) values contained in all nested monads
+ #
+ # [1] == [[Some(Some(1)), Some(Some(None))], [None]].flatten
+ # => true
+ #
def flatten_with_monad
bind { |x| x.is_a?(Monad) ? x.flatten_with_monad : self.class.unit(x) }
+ end
+
+ # Returns a monad whose elements are all those elements of this monad for which the given predicate returned true
+ def select(lam = nil, &blk)
+ bind { |x| (lam || blk).call(x) ? self.class.unit(x) : self.class.empty }
+ end
+ alias_method :find_all, :select
+
+ # 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).
+ #
+ def shallow_flatten
+ bind { |x| x.is_a?(Monad) ? x : self.class.unit(x) }
end
end
end