Feature #5007

Proc#call_under: Unifying instance_eval and instance_exec

Added by Magnus Holm almost 3 years ago. Updated 10 months ago.

[ruby-core:37924]
Status:Assigned
Priority:Normal
Assignee:Yukihiro Matsumoto
Category:-
Target version:next minor

Description

I'm proposing a method called Proc#callunder (the name could be
discussed) which both unifies instance
eval and instance_exec, and makes
it possible to call a Proc with a block and a scope:

Proc#call_under(self, *args, &blk):

proc { self }.call_under(1) # => 1

proc { |a| self + a }.call_under(1, 2) # => 3

proc { |&b| self + b.call }.call_under(2) { 2 } # => 4

proc_call_under.patch Magnifier (2.06 KB) Yusuke Endoh, 04/17/2012 10:52 PM


Related issues

Related to ruby-trunk - Feature #6298: Proc#+ Rejected 04/15/2012

History

#1 Updated by Yusuke Endoh about 2 years ago

  • Assignee set to Yukihiro Matsumoto
  • Status changed from Open to Assigned

#2 Updated by Thomas Sawyer about 2 years ago

=begin
I don't see how this solves the case presented in #6298.

Given:

class BlockCollection
def initialize(procs)
@procs = procs
end
def to_proc
procs = @procs
Proc.new{ |
a| procs.each{ |p| p.call_under(self, *a) } }
end
end

What is self here, or what should it be? Such that:

module M
A = Proc.new{ self }
end

bc = BlockCollection.new(A)

bc.call #=> M

'F'.instance_eval(&bc) #=> 'F'

=end

#3 Updated by Yusuke Endoh about 2 years ago

Hello,

I made a proof-of-concept patch.
Not tested yet. Please try it and find a bug.
It (and some related functions) seem to need some refactoring work
because it calls directly invokeblockfrom_c which is very internal
function.

$ ./miniruby -e '
p proc { self }.callunder(1)
p proc { |a| self + a }.call
under(1, 2)
p proc { |&b| self + b.call }.call_under(2) { 2 }
'
1
3
4

diff --git a/proc.c b/proc.c
index d44e8d8..7ad490e 100644
--- a/proc.c
+++ b/proc.c
@@ -567,6 +567,22 @@ proc_call(int argc, VALUE *argv, VALUE procval)
return vret;
}

+VALUE rbproccallunder(VALUE procval, VALUE under, VALUE self, VALUE values);
+
+static VALUE
+proc
callunder(int argc, VALUE *argv, VALUE procval)
+{
+ VALUE self, klass, values;
+ rb
scanargs(argc, argv, "1*", &self, &values);
+ if (SPECIAL
CONSTP(self)) {
+ klass = Qnil;
+ }
+ else {
+ klass = rb
singletonclass(self);
+ }
+ return rb
proccallunder(procval, klass, self, values);
+}
+
#if SIZEOFLONG > SIZEOFINT
static inline int
checkargc(long argc)
@@ -2183,6 +2199,7 @@ Init
Proc(void)
rbdefinemethod(rbcProc, "[]", proccall, -1);
rbdefinemethod(rbcProc, "===", proccall, -1);
rbdefinemethod(rbcProc, "yield", proccall, -1);
+ rbdefinemethod(rbcProc, "callunder", proccallunder, -1);
#endif
rbdefinemethod(rbcProc, "toproc", proctoproc, 0);
rbdefinemethod(rbcProc, "arity", procarity, 0);
diff --git a/vmeval.c b/vmeval.c
index 6c26b97..562a215 100644
--- a/vmeval.c
+++ b/vm
eval.c
@@ -1262,6 +1262,30 @@ yield_under(VALUE under, VALUE self, VALUE values)
}
}

+static inline VALUE
+invokeblockfromc(rbthreadt *th, const rbblockt *block,
+ VALUE self, int argc, const VALUE *argv,
+ const rb
blockt *blockptr, const NODE *cref);
+
+VALUE
+rb
proccallunder(VALUE procval, VALUE under, VALUE self, VALUE values)
+{
+ rbthreadt th = GETTHREAD();
+ rb
blockt block;
+ NODE *cref;
+ rb
proct *proc;
+
+ GetProcPtr(procval, proc);
+ block = proc->block;
+ block.self = self;
+ cref = vm
crefpush(th, under, NOEXPUBLIC, &proc->block);
+ cref->flags |= NODEFLCREFPUSHEDBYEVAL;
+
+ return invoke
blockfromc(th, &block, self,
+ RARRAYLENINT(values), RARRAYPTR(values),
+ GCGUARDEDPTR_REF(th->cfp->lfp[0]), cref);
+}
+
/
string eval under the class/module context */
static VALUE
eval_under(VALUE under, VALUE self, VALUE src, const char *file, int line)

Yusuke Endoh mame@tsg.ne.jp

#4 Updated by Yusuke Endoh over 1 year ago

  • Target version set to next minor

#5 Updated by Magnus Holm 10 months ago

I concur with trans: I don't think this issue will solve #6298. However, please keep that discussion out of this issue.

Would it be possible to get a OK/NG for inclusion of this in 2.1?

Still not sure about the name. Maybe #call_with is better?

#6 Updated by Matthew Kerwin 10 months ago

judofyr (Magnus Holm) wrote:

Still not sure about the name. Maybe #call_with is better?

I suggest #callas or #callbound: "as" because it suggests that "self" in the proc will refer to the first parameter, or possibly "bound" because we're binding the proc to the object (as well as to its existing binding scope). However I don't have strong feelings about any name, except that I like #call_with less than any others.

A side discussion: how does this tie in with currying? For example, do you foresee a Proc#curryunder (or curryself, or currywith, or whatever) method, that would cause the proc to be passed to instanceexec when all its parameters are satisfied?

Also available in: Atom PDF