module Functional::PatternMatching

As much as I love Ruby I’ve always been a little disappointed that Ruby doesn’t support function overloading. Function overloading tends to reduce branching and keep function signatures simpler. No sweat, I learned to do without. Then I started programming in Erlang. My favorite Erlang feature is, without question, pattern matching. Pattern matching is like function overloading cranked to 11. So one day I was musing on Twitter that I’d like to see Erlang-stype pattern matching in Ruby and one of my friends responded “Build it!” So I did. And here it is.

{include:file:doc/pattern_matching.md}

Constants

ALL

A match for one or more parameters in the last position of the match. @!visibility private

FunctionPattern

@!visibility private

GuardClause

A guard clause on a pattern match. @!visibility private

UNBOUND

A parameter that is required but that can take any value. @!visibility private

Private Class Methods

included(base) click to toggle source

@!visibility private

Calls superclass method
# File lib/functional/pattern_matching.rb, line 83
def self.included(base)
  base.extend(ClassMethods)
  super(base)
end

Private Instance Methods

__pass_guard__?(matcher, args) click to toggle source
# File lib/functional/pattern_matching.rb, line 68
def __pass_guard__?(matcher, args)
  matcher.guard.nil? ||
    self.instance_exec(*__unbound_args__(matcher, args), &matcher.guard)
end
__pattern_match__(clazz, function, *args, &block) click to toggle source

@!visibility private

# File lib/functional/pattern_matching.rb, line 74
def __pattern_match__(clazz, function, *args, &block)
  args = args.first
  matchers = clazz.__function_pattern_matches__.fetch(function, [])
  matchers.detect do |matcher|
    MethodSignature.match?(matcher.args, args) && __pass_guard__?(matcher, args)
  end
end
__unbound_args__(match, args) click to toggle source

@!visibility private

# File lib/functional/pattern_matching.rb, line 50
def __unbound_args__(match, args)
  argv = []
  match.args.each_with_index do |p, i|
    if p == ALL && i == match.args.length-1
      # when got ALL, then push all to the end to the list of args,
      # so we can get them as usual *args in matched method
      argv.concat args[(i..args.length)]
    elsif p.is_a?(Hash) && p.values.include?(UNBOUND)
      p.each do |key, value|
        argv << args[i][key] if value == UNBOUND
      end
    elsif p.is_a?(Hash) || p == UNBOUND || p.is_a?(Class)
      argv << args[i]
    end
  end
  argv
end