Skip to content

Instantly share code, notes, and snippets.

@jah2488
Last active August 29, 2015 13:56
Show Gist options
  • Save jah2488/8945551 to your computer and use it in GitHub Desktop.
Save jah2488/8945551 to your computer and use it in GitHub Desktop.
Hey look, its multi method body(s) in ruby, method overloading really. Now you can define a bunch of methods with different arity and call them from a single place without worry*
class Object
def defn(name, &block)
@name = name
@fns = []
instance_eval(&block)
self.class.send(:define_method, @name, Proc.new {|*args|
not_found = -> { raise "no method #{name.to_sym} found with #{args.count} args" }
@fns.detect(not_found) { |fn| fn.fetch(:arity) == args.count }.fetch(:proc).call(*args)
})
end
def fn(&block)
@fns << {:arity => block.arity, :proc => block}
end
end
if $0 !~ /rspec/
begin require 'pry'; binding.pry rescue Gem::LoadError end
else
describe 'defn' do
context 'creates callable multimethod' do
before(:each) do
defn(:foo) do |f|
fn { 'hi' }
fn { |x| "Hello, #{x}" }
fn { |x, y| "Howdy, #{x} and #{y}" }
end
end
it { expect(foo).to eq('hi') }
it { expect(foo 'justin').to eq('Hello, justin') }
it { expect(foo 'jane', 'carol').to eq('Howdy, jane and carol') }
it { expect{foo 'jane', 'carol', 'steve' }.to raise_error 'no method foo found with 3 args'}
end
end
end
defn(:greet) do
fn { 'hi' }
fn do |x|
'Hello ' + x
end
fn do |x, y|
"Howdy, #{x} and #{y}"
end
end
greet #=> 'hi'
greet('justin') #=> 'Hello justin'
#vs
def greet
'hi'
end
def greet_with_name(name)
'Hello ' + x
end
def greet_me_and_them(me, them)
"Howdy, #{me} and #{them}"
end
#vs
def greet(*args)
case args.count
when 0 then 'hi'
when 1 then 'Hello ' + args[0]
when 2 then "Howdy, #{args[0]} and #{args[1]}"
end
end
@jah2488
Copy link
Author

jah2488 commented Feb 11, 2014

*There is some worry.

**Has all the limitations of procs vs methods

***Probably contains some weird side effects

@jah2488
Copy link
Author

jah2488 commented Feb 12, 2014

Right now splat arguments won't work, as its expecting a finite number of arguments for each method, but I did find a way to work in splat argument method bodies, but it'll take some time and add a lot of complexity to the script. Perhaps I shall save that for later.

@jah2488
Copy link
Author

jah2488 commented Feb 12, 2014

Now that a single method with multiple method bodies is out of the way. I think the next evolution should be a true multimethod equivalent. A way to define a context and then execute a method body based on that context.

Perhaps some syntax like:

defmulti greet { |x| x.class }
defmethod Bob, { 'hey bobby' }
defmethod Jan, { 'Hello Jan' }
defmethod String, do 
  'Hello you'
end

Idk, should be fun though

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment