New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Proc#* and Method#* for Proc and Method composition #935
Conversation
proc.c
Outdated
VALUE f, g, fargs; | ||
f = RARRAY_AREF(args, 0); | ||
g = RARRAY_AREF(args, 1); | ||
fargs = rb_ary_new3(1, rb_proc_call_with_block(g, argc, argv, passed_proc)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we could change this to send call
to g
with the given arguments (e.g. via rb_funcall
), this would work with any object with a call
method (including other Proc
s, Method
s and regular Ruby classes).
Alternatively, we could ensure g
is a Proc
by calling to_proc
on it if it is not already a Proc
(not entirely dissimilar to using the existing StringValue
macro).
As discussed on the issue tracker, this would mean the difference between the following Ruby pseudocode:
# Where g must only implement call
class Proc
def *(g)
proc { |*args, &blk| call(g.call(*args, &blk)) }
end
end
vs
# Where g must only implement to_proc
class Proc
def *(g)
proc { |*args, &blk| call(g.to_proc.call(*args, &blk)) }
end
end
I, for one, would love to see this merged! :) |
I like the idea of functional composition but I don't like the implementation here. First of all, I don't like naming it And the second problem that it does not deal well with custom callable objects. I mean: module Increment
def self.call(x)
x + 1
end
end
multiply_two = proc { |x| x * 2 }
(multiply_two * Increment).call(1) # => 4
(Increment * multiply_two).call(1) # => NoMethodError So, I like the concept of mixin with composition operator more like it's done in module Increment
extend PipelineOperator
def self.call(x)
x + 1
end
end |
* proc.c (proc_compose): Implement Proc#* for Proc composition, enabling composition of Procs and Methods. [Feature ruby#6284] * test/ruby/test_proc.rb: Add test cases for Proc composition.
* proc.c (rb_method_compose): Implement Method#* for Method composition, which delegates to Proc#*. * test/ruby/test_method.rb: Add test cases for Method composition.
* proc.c (proc_compose): support any object with a call method rather than supporting only procs. [Feature ruby#6284] * proc.c (compose): use the function call on the given object rather than rb_proc_call_with_block in order to support any object. * test/ruby/test_proc.rb: Add test cases for composing Procs with callable objects. * test/ruby/test_method.rb: Add test cases for composing Methods with callable objects.
FWIW I am pro |
It seems to have a conflict now. Could you rebase this from master? |
Oh, I see. Thank you 👍 |
c.f. https://bugs.ruby-lang.org/issues/6284
Allow
Proc
s andMethod
s to be composed together with*
.Support composition with any object that has a
call
method, e.g.This implementation should be largely equivalent to the following Ruby (excepting that the
lambda?
property is preserved):