In Files

  • proc.c

Proc

Proc objects are blocks of code that have been bound to a set of local variables. Once bound, the code may be called in different contexts and still access those variables.

def gen_times(factor)
  return Proc.new {|n| n*factor }
end

times3 = gen_times(3)
times5 = gen_times(5)

times3.call(12)               #=> 36
times5.call(5)                #=> 25
times3.call(times5.call(4))   #=> 60

Public Class Methods

new {|...| block } => a_proc click to toggle source
new => a_proc

Creates a new Proc object, bound to the current context. Proc::new may be called without a block only within a method with an attached block, in which case that block is converted to the Proc object.

def proc_from
  Proc.new
end
proc = proc_from { "hello" }
proc.call   #=> "hello"
 
               static VALUE
rb_proc_s_new(int argc, VALUE *argv, VALUE klass)
{
    VALUE block = proc_new(klass, Qfalse);

    rb_obj_call_init(block, argc, argv);
    return block;
}
            

Public Instance Methods

prc == other_proc => true or false click to toggle source

Return true if prc is the same object as other_proc, or if they are both procs with the same body.

 
               static VALUE
proc_eq(VALUE self, VALUE other)
{
    if (self == other) {
        return Qtrue;
    }
    else {
        if (TYPE(other)          == T_DATA &&
            RDATA(other)->dmark  == proc_mark) {
            rb_proc_t *p1, *p2;
            GetProcPtr(self, p1);
            GetProcPtr(other, p2);
            if (p1->envval == p2->envval &&
                p1->block.iseq->iseq_size == p2->block.iseq->iseq_size &&
                p1->block.iseq->local_size == p2->block.iseq->local_size &&
                MEMCMP(p1->block.iseq->iseq, p2->block.iseq->iseq, VALUE,
                       p1->block.iseq->iseq_size) == 0) {
                return Qtrue;
            }
        }
    }
    return Qfalse;
}
            
prc === obj => obj click to toggle source

Invokes the block, with obj as the block's parameter. It is to allow a proc object to be a target of when clause in the case statement.

 
               static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
    rb_proc_t *proc;
    rb_block_t *blockptr = 0;
    rb_iseq_t *iseq;
    GetProcPtr(procval, proc);

    iseq = proc->block.iseq;
    if (BUILTIN_TYPE(iseq) == T_NODE || iseq->arg_block != -1) {
        if (rb_block_given_p()) {
            rb_proc_t *proc;
            VALUE procval;
            procval = rb_block_proc();
            GetProcPtr(procval, proc);
            blockptr = &proc->block;
        }
    }

    return rb_vm_invoke_proc(GET_THREAD(), proc, proc->block.self,
                             argc, argv, blockptr);
}
            
prc === obj => obj click to toggle source

Invokes the block, with obj as the block's parameter. It is to allow a proc object to be a target of when clause in the case statement.

 
               static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
    rb_proc_t *proc;
    rb_block_t *blockptr = 0;
    rb_iseq_t *iseq;
    GetProcPtr(procval, proc);

    iseq = proc->block.iseq;
    if (BUILTIN_TYPE(iseq) == T_NODE || iseq->arg_block != -1) {
        if (rb_block_given_p()) {
            rb_proc_t *proc;
            VALUE procval;
            procval = rb_block_proc();
            GetProcPtr(procval, proc);
            blockptr = &proc->block;
        }
    }

    return rb_vm_invoke_proc(GET_THREAD(), proc, proc->block.self,
                             argc, argv, blockptr);
}
            
arity → fixnum click to toggle source

Returns the number of arguments that would not be ignored. If the block is declared to take no arguments, returns 0. If the block is known to take exactly n arguments, returns n. If the block has optional arguments, return -n-1, where n is the number of mandatory arguments. A proc with no argument declarations is the same a block declaring || as its arguments.

Proc.new {}.arity          #=>  0
Proc.new {||}.arity        #=>  0
Proc.new {|a|}.arity       #=>  1
Proc.new {|a,b|}.arity     #=>  2
Proc.new {|a,b,c|}.arity   #=>  3
Proc.new {|*a|}.arity      #=> -1
Proc.new {|a,*b|}.arity    #=> -2
Proc.new {|a,*b, c|}.arity    #=> -3
 
               static VALUE
proc_arity(VALUE self)
{
    rb_proc_t *proc;
    rb_iseq_t *iseq;
    GetProcPtr(self, proc);
    iseq = proc->block.iseq;
    if (iseq) {
        if (BUILTIN_TYPE(iseq) != T_NODE) {
            if (iseq->arg_rest < 0) {
                return INT2FIX(iseq->argc);
            }
            else {
                return INT2FIX(-(iseq->argc + 1 + iseq->arg_post_len));
            }
        }
        else {
            NODE *node = (NODE *)iseq;
            if (nd_type(node) == NODE_IFUNC && node->nd_cfnc == bmcall) {
                /* method(:foo).to_proc.arity */
                return INT2FIX(method_arity(node->nd_tval));
            }
        }
    }
    return INT2FIX(-1);
}
            
binding => binding click to toggle source

Returns the binding associated with prc. Note that Kernel#eval accepts either a Proc or a Binding object as its second parameter.

def fred(param)
  proc {}
end

b = fred(99)
eval("param", b.binding)   #=> 99
 
               static VALUE
proc_binding(VALUE self)
{
    rb_proc_t *proc;
    VALUE bindval = binding_alloc(rb_cBinding);
    rb_binding_t *bind;

    GetProcPtr(self, proc);
    GetBindingPtr(bindval, bind);

    if (TYPE(proc->block.iseq) == T_NODE) {
        rb_raise(rb_eArgError, "Can't create Binding from C level Proc");
    }

    bind->env = proc->envval;
    return bindval;
}
            
prc === obj => obj click to toggle source

Invokes the block, with obj as the block's parameter. It is to allow a proc object to be a target of when clause in the case statement.

 
               static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
    rb_proc_t *proc;
    rb_block_t *blockptr = 0;
    rb_iseq_t *iseq;
    GetProcPtr(procval, proc);

    iseq = proc->block.iseq;
    if (BUILTIN_TYPE(iseq) == T_NODE || iseq->arg_block != -1) {
        if (rb_block_given_p()) {
            rb_proc_t *proc;
            VALUE procval;
            procval = rb_block_proc();
            GetProcPtr(procval, proc);
            blockptr = &proc->block;
        }
    }

    return rb_vm_invoke_proc(GET_THREAD(), proc, proc->block.self,
                             argc, argv, blockptr);
}
            
clone() click to toggle source
 
               static VALUE
proc_clone(VALUE self)
{
    VALUE procval = proc_dup(self);
    CLONESETUP(procval, self);
    return procval;
}
            
curry => a_proc click to toggle source
curry(arity) => a_proc

Returns a curried proc. If the optional arity argument is given, it determines the number of arguments. A curried proc receives some arguments. If a sufficient number of arguments are supplied, it passes the supplied arguments to the original proc and returns the result. Otherwise, returns another curried proc that takes the rest of arguments.

b = proc {|x, y, z| (x||0) + (y||0) + (z||0) }
p b.curry[1][2][3]           #=> 6
p b.curry[1, 2][3, 4]        #=> 6
p b.curry(5)[1][2][3][4][5]  #=> 6
p b.curry(5)[1, 2][3, 4][5]  #=> 6
p b.curry(1)[1]              #=> 1

b = proc {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
p b.curry[1][2][3]           #=> 6
p b.curry[1, 2][3, 4]        #=> 10
p b.curry(5)[1][2][3][4][5]  #=> 15
p b.curry(5)[1, 2][3, 4][5]  #=> 15
p b.curry(1)[1]              #=> 1

b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) }
p b.curry[1][2][3]           #=> 6
p b.curry[1, 2][3, 4]        #=> wrong number of arguments (4 or 3)
p b.curry(5)                 #=> wrong number of arguments (5 or 3)
p b.curry(1)                 #=> wrong number of arguments (1 or 3)

b = lambda {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
p b.curry[1][2][3]           #=> 6
p b.curry[1, 2][3, 4]        #=> 10
p b.curry(5)[1][2][3][4][5]  #=> 15
p b.curry(5)[1, 2][3, 4][5]  #=> 15
p b.curry(1)                 #=> wrong number of arguments (1 or 3)

b = proc { :foo }
p b.curry[]                  #=> :foo
 
               static VALUE
proc_curry(int argc, VALUE *argv, VALUE self)
{
    int sarity, marity = FIX2INT(proc_arity(self));
    VALUE arity, opt = Qfalse;

    if (marity < 0) {
        marity = -marity - 1;
        opt = Qtrue;
    }

    rb_scan_args(argc, argv, "01", &arity);
    if (NIL_P(arity)) {
        arity = INT2FIX(marity);
    }
    else {
        sarity = FIX2INT(arity);
        if (proc_lambda_p(self) && (sarity < marity || (sarity > marity && !opt))) {
            rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", sarity, marity);
        }
    }

    return make_curry_proc(self, rb_ary_new(), arity);
}
            
dup() click to toggle source
 
               static VALUE
proc_dup(VALUE self)
{
    VALUE procval = rb_proc_alloc(rb_cProc);
    rb_proc_t *src, *dst;
    GetProcPtr(self, src);
    GetProcPtr(procval, dst);

    dst->block = src->block;
    dst->block.proc = procval;
    dst->envval = src->envval;
    dst->safe_level = src->safe_level;
    dst->is_lambda = src->is_lambda;

    return procval;
}
            
prc == other_proc => true or false click to toggle source

Return true if prc is the same object as other_proc, or if they are both procs with the same body.

 
               static VALUE
proc_eq(VALUE self, VALUE other)
{
    if (self == other) {
        return Qtrue;
    }
    else {
        if (TYPE(other)          == T_DATA &&
            RDATA(other)->dmark  == proc_mark) {
            rb_proc_t *p1, *p2;
            GetProcPtr(self, p1);
            GetProcPtr(other, p2);
            if (p1->envval == p2->envval &&
                p1->block.iseq->iseq_size == p2->block.iseq->iseq_size &&
                p1->block.iseq->local_size == p2->block.iseq->local_size &&
                MEMCMP(p1->block.iseq->iseq, p2->block.iseq->iseq, VALUE,
                       p1->block.iseq->iseq_size) == 0) {
                return Qtrue;
            }
        }
    }
    return Qfalse;
}
            
hash => integer click to toggle source

Return hash value corresponding to proc body.

 
               static VALUE
proc_hash(VALUE self)
{
    int hash;
    rb_proc_t *proc;
    GetProcPtr(self, proc);
    hash = (long)proc->block.iseq;
    hash ^= (long)proc->envval;
    hash ^= (long)proc->block.lfp >> 16;
    return INT2FIX(hash);
}
            
lambda? => true or false click to toggle source

Returns true for a Proc object which argument handling is rigid. Such procs are typically generated by lambda.

A Proc object generated by proc ignore extra arguments.

proc {|a,b| [a,b] }.call(1,2,3)    => [1,2]

It provides nil for lacked arguments.

proc {|a,b| [a,b] }.call(1)        => [1,nil]

It expand single-array argument.

proc {|a,b| [a,b] }.call([1,2])    => [1,2]

A Proc object generated by lambda doesn't have such tricks.

lambda {|a,b| [a,b] }.call(1,2,3)  => ArgumentError
lambda {|a,b| [a,b] }.call(1)      => ArgumentError
lambda {|a,b| [a,b] }.call([1,2])  => ArgumentError

#lambda? is a predicate for the tricks. It returns true if no tricks.

lambda {}.lambda?          => true
proc {}.lambda?            => false

::new is same as proc.

Proc.new {}.lambda?        => false

lambda, proc and ::new preserves the tricks of a Proc object given by & argument.

lambda(&lambda {}).lambda?   => true
proc(&lambda {}).lambda?     => true
Proc.new(&lambda {}).lambda? => true

lambda(&proc {}).lambda?   => false
proc(&proc {}).lambda?     => false
Proc.new(&proc {}).lambda? => false

A Proc object generated by & argument has the tricks

def n(&b) b.lambda? end
n {}                       => false

The & argument preserves the tricks if a Proc object is given by & argument.

n(&lambda {})              => true
n(&proc {})                => false
n(&Proc.new {})            => false

A Proc object converted from a method has no tricks.

def m() end
method(:m).to_proc.lambda? => true

n(&method(:m))             => true
n(&method(:m).to_proc)     => true

define_method is treated same as method definition. The defined method has no tricks.

class C
  define_method(:d) {}
end
C.new.e(1,2)       => ArgumentError
C.new.method(:d).to_proc.lambda?   => true

define_method always defines a method without the tricks, even if a non-lambda Proc object is given. This is the only exception which the tricks are not preserved.

class C
  define_method(:e, &proc {})
end
C.new.e(1,2)       => ArgumentError
C.new.method(:e).to_proc.lambda?   => true

This exception is for a wrapper of define_method. It eases defining a method defining method which defines a usual method which has no tricks.

class << C
  def def2(name, &body)
    define_method(name, &body)
  end
end
class C
  def2(:f) {}
end
C.new.f(1,2)       => ArgumentError

The wrapper, def2, defines a method which has no tricks.

 
               static VALUE
proc_lambda_p(VALUE procval)
{
    rb_proc_t *proc;
    GetProcPtr(procval, proc);

    return proc->is_lambda ? Qtrue : Qfalse;
}
            
source_location => [String, Fixnum] click to toggle source

returns the ruby source filename and line number containing this proc or nil if this proc was not defined in ruby (i.e. native)

 
               VALUE
rb_proc_location(VALUE self)
{
    return iseq_location(get_proc_iseq(self));
}
            
to_proc → prc click to toggle source

Part of the protocol for converting objects to Proc objects. Instances of class Proc simply return themselves.

 
               static VALUE
proc_to_proc(VALUE self)
{
    return self;
}
            
to_s => string click to toggle source

Shows the unique identifier for this proc, along with an indication of where the proc was defined.

 
               static VALUE
proc_to_s(VALUE self)
{
    VALUE str = 0;
    rb_proc_t *proc;
    const char *cname = rb_obj_classname(self);
    rb_iseq_t *iseq;
    const char *is_lambda;
    
    GetProcPtr(self, proc);
    iseq = proc->block.iseq;
    is_lambda = proc->is_lambda ? " (lambda)" : "";

    if (RUBY_VM_NORMAL_ISEQ_P(iseq)) {
        int line_no = 0;
        
        if (iseq->insn_info_table) {
            line_no = rb_iseq_first_lineno(iseq);
        }
        str = rb_sprintf("#<%s:%p@%s:%d%s>", cname, (void *)self,
                         RSTRING_PTR(iseq->filename),
                         line_no, is_lambda);
    }
    else {
        str = rb_sprintf("#<%s:%p%s>", cname, (void *)proc->block.iseq,
                         is_lambda);
    }

    if (OBJ_TAINTED(self)) {
        OBJ_TAINT(str);
    }
    return str;
}
            
prc === obj => obj click to toggle source

Invokes the block, with obj as the block's parameter. It is to allow a proc object to be a target of when clause in the case statement.

 
               static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
    rb_proc_t *proc;
    rb_block_t *blockptr = 0;
    rb_iseq_t *iseq;
    GetProcPtr(procval, proc);

    iseq = proc->block.iseq;
    if (BUILTIN_TYPE(iseq) == T_NODE || iseq->arg_block != -1) {
        if (rb_block_given_p()) {
            rb_proc_t *proc;
            VALUE procval;
            procval = rb_block_proc();
            GetProcPtr(procval, proc);
            blockptr = &proc->block;
        }
    }

    return rb_vm_invoke_proc(GET_THREAD(), proc, proc->block.self,
                             argc, argv, blockptr);
}