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
@!visibility private
# File lib/functional/pattern_matching.rb, line 83 def self.included(base) base.extend(ClassMethods) super(base) end
Private Instance Methods
# 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
@!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
@!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