EdvardM
1/19/2015 - 3:48 PM

Simple maybe for Ruby

Simple maybe for Ruby

# mimic behaviour of Haskell, idea taken mostly from
# https://github.com/tomstuart/monads/

Maybe = Struct.new(:value) do
  # map value to monadic context, unless it's already
  def self.unit(value)
    value.kind_of?(Maybe) ? value : new(value)
  end

  # maybes = [nil, 1, 2].map { |i| Maybe.new(i) }
  # Maybe.detect_m(maybes) { |i| i.even? }.value # 2
  def self.detect_m(lst, &orig_block)
    block = orig_block ? orig_block : -> (x) { x }
    unit(
      lst.detect do |m_item|
        ensure_monadic(m_item).bind { |v| block.call(v) }.value
      end
    )
  end

  def bind(&block)
    value.nil? ? self : self.class.unit(block.call(value))
  end
  alias >> bind

  private

  NonMonadicValue = Class.new(StandardError)
  def self.ensure_monadic(item)
    item.tap do
      raise NonMonadicValue, item unless item.kind_of?(Maybe)
    end
  end
end