From 1ab235eec02cdd65577e0e9ad5800bd856a68381 Mon Sep 17 00:00:00 2001 From: Arne Brasseur Date: Tue, 29 Apr 2014 13:36:14 +0800 Subject: [PATCH] Implement Method#curry, which delegates to to_proc.curry --- proc.c | 39 +++++++++++++++++++++++++++++++++++++++ test/ruby/test_method.rb | 18 ++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/proc.c b/proc.c index e3cecb7..afda02b 100644 --- a/proc.c +++ b/proc.c @@ -1803,6 +1803,44 @@ rb_method_call(int argc, VALUE *argv, VALUE method) return rb_method_call_with_block(argc, argv, method, proc); } +/* + * call-seq: + * meth.curry -> proc + * meth.curry(arity) -> proc + * + * Returns a curried proc based on the method. When the proc is called with a number of + * arguments that is lower than the method's arity, then another curried proc is returned. + * Only when enough arguments have been supplied to satisfy the method signature, will the + * method actually be called. + * + * The optional arity argument should be supplied when currying methods with + * variable arguments to determine how many arguments are needed before the method is + * called. + * + * def foo(a,b,c) + * [a, b, c] + * end + * + * proc = self.method(:foo).curry + * proc2 = proc.call(1, 2) #=> # + * proc2.call(3) #=> [1,2,3] + * + * def vararg(*args) + * args + * end + * + * proc = self.method(:vararg).curry(4) + * proc2 = proc.call(:x) #=> # + * proc3 = proc2.call(:y, :z) #=> # + * proc3.call(:a) #=> [:x, :y, :z, :a] + */ +VALUE +rb_method_curry(int argc, VALUE *argv, VALUE self) +{ + VALUE proc = rb_funcall(self, rb_intern("to_proc"), 0); + return rb_funcall2(proc, rb_intern("curry"), argc, argv); +} + VALUE rb_method_call_with_block(int argc, VALUE *argv, VALUE method, VALUE pass_procval) { @@ -2632,6 +2670,7 @@ Init_Proc(void) rb_define_method(rb_cMethod, "hash", method_hash, 0); rb_define_method(rb_cMethod, "clone", method_clone, 0); rb_define_method(rb_cMethod, "call", rb_method_call, -1); + rb_define_method(rb_cMethod, "curry", rb_method_curry, -1); rb_define_method(rb_cMethod, "[]", rb_method_call, -1); rb_define_method(rb_cMethod, "arity", method_arity_m, 0); rb_define_method(rb_cMethod, "inspect", method_inspect, 0); diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index 2e3c2ae..b8a3cd6 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -754,4 +754,22 @@ class TestMethod < Test::Unit::TestCase m = assert_nothing_raised(NameError, feature8391) {break o.singleton_method(:bar)} assert_equal(:bar, m.call, feature8391) end + + def test_curry + c = Class.new + p = proc {|a,b,c| a + b + c } + c.class_eval { define_method(:three_args, p) } + curried = c.new.method(:three_args).curry + + assert_equal(6, curried.(1).(2).(3)) + end + + def test_curry_arity + c = Class.new + p = proc {|*args| args } + c.class_eval { define_method(:var_args, p) } + curried = c.new.method(:var_args).curry(3) + + assert_equal([1, 2, 3], curried.(1).(2).(3)) + end end -- 1.9.1