proc {} vs. Method#to_proc 
Author Message
 proc {} vs. Method#to_proc

Hi --

Arising from a recent discussion on #ruby-lang:

Proc objects created by Method#to_proc seem to care about their arity
in a way that other Proc objects don't.  To illustrate:

  class A
    def talk
      puts "hi from A#talk"
    end
  end

  pr = Proc.new { puts "hi from anonymous Proc" }
  methproc = A.new.method(:talk).to_proc

  [1,2,3].each &pr         # "hi from anonymous Proc\n" * 3
  [1,2,3].each &methproc   # in `talk': wrong # of arguments(1 for 0)

The #to_proc proc is reacting like a method, not a proc, when given
the "wrong" number of arguments.  For me, this doesn't fit in with the
idea of (complete) conversion to a proc.

Any other thoughts or interpretations of this behavior?

David

--
David Alan Black


Web:   http://www.*-*-*.com/ ~blackdav



Tue, 09 Aug 2005 00:19:17 GMT  
 proc {} vs. Method#to_proc

Quote:

> Hi --

> Arising from a recent discussion on #ruby-lang:

> Proc objects created by Method#to_proc seem to care about their arity
> in a way that other Proc objects don't.  To illustrate:

>   class A
>     def talk
>       puts "hi from A#talk"
>     end
>   end

>   pr = Proc.new { puts "hi from anonymous Proc" }
>   methproc = A.new.method(:talk).to_proc

>   [1,2,3].each &pr         # "hi from anonymous Proc\n" * 3
>   [1,2,3].each &methproc   # in `talk': wrong # of arguments(1 for 0)

Interesting. I would have just done

  [1,2,3].each { methproc.call }

and never have noticed. Can you explain what is happening when
I replace {...} with &pr.

Quote:
> The #to_proc proc is reacting like a method, not a proc, when given
> the "wrong" number of arguments.  For me, this doesn't fit in with the
> idea of (complete) conversion to a proc.

--
Jim Freeze
----------
If God had intended Man to Walk, He would have given him Feet.


Tue, 09 Aug 2005 01:17:01 GMT  
 proc {} vs. Method#to_proc

Quote:


> >   pr = Proc.new { puts "hi from anonymous Proc" }
> >   methproc = A.new.method(:talk).to_proc

> >   [1,2,3].each &pr         # "hi from anonymous Proc\n" * 3
> >   [1,2,3].each &methproc   # in `talk': wrong # of arguments(1 for 0)

> Interesting. I would have just done

>   [1,2,3].each { methproc.call }

> and never have noticed. Can you explain what is happening when
> I replace {...} with &pr.

& turns a proc into a block.

--

All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E  7C11 332A 551C 796C 9F04

  application_pgp-signature_part
< 1K Download


Tue, 09 Aug 2005 02:38:30 GMT  
 proc {} vs. Method#to_proc

Quote:



> > >   pr = Proc.new { puts "hi from anonymous Proc" }
> > >   methproc = A.new.method(:talk).to_proc

> > >   [1,2,3].each &pr         # "hi from anonymous Proc\n" * 3
> > >   [1,2,3].each &methproc   # in `talk': wrong # of arguments(1 for 0)

> > Interesting. I would have just done

> >   [1,2,3].each { methproc.call }

> > and never have noticed. Can you explain what is happening when
> > I replace {...} with &pr.

> & turns a proc into a block.

So why the error wrong # of arguments?

--
Jim Freeze
----------
Always borrow money from a pessimist; he doesn't expect to be paid
back.



Tue, 09 Aug 2005 03:10:41 GMT  
 proc {} vs. Method#to_proc

Quote:

> Hi --

> Arising from a recent discussion on #ruby-lang:

> Proc objects created by Method#to_proc seem to care about their arity
> in a way that other Proc objects don't.  To illustrate:

Procs don't care about the arity when they are transformed into blocks
with '&': unneeded arguments are discarded and if there's not enough
they get filled with nil.

Quote:
>> p = proc { |a,b,c,d,e| p(a) }

=> #<Proc:0x402ab4b4>
Quote:
>> [1,2,3].each &p

(irb):4: warning: `&' interpreted as argument prefix
1
2
3
=> [1, 2, 3]
Quote:
>> p.call(1)

ArgumentError: wrong # of arguments (1 for 5)
        from (irb):5
        from (irb):3:in `call'
        from (irb):5

Quote:
>   class A
>     def talk
>       puts "hi from A#talk"
>     end
>   end

>   pr = Proc.new { puts "hi from anonymous Proc" }
>   methproc = A.new.method(:talk).to_proc

>   [1,2,3].each &pr         # "hi from anonymous Proc\n" * 3
>   [1,2,3].each &methproc   # in `talk': wrong # of arguments(1 for 0)

> The #to_proc proc is reacting like a method, not a proc, when given
> the "wrong" number of arguments.  For me, this doesn't fit in with the
> idea of (complete) conversion to a proc.

> Any other thoughts or interpretations of this behavior?

Method#to_proc seems to be working like this:

class Method
  def my_to_proc
    proc { |*args| self.call(*args) }
  end
end

class A
  def talk
    puts "A#talk"
  end
end

methproc = A.new.method(:talk).my_to_proc
[1,2,3].each(&methproc)


j.rb:4:in `talk': wrong # of arguments(1 for 0) (ArgumentError)
        from j.rb:4:in `call'
        from j.rb:4:in `my_to_proc'
        from j.rb:4:in `each'
        from j.rb:15

You can make it behave like a "real" proc with the following:


class Method
  def my_to_proc
    case
    when arity > 0
      proc do |*args|
        (arity - args.size).times() { args << nil } if arity > args.size
        self.call(*(args[0,arity]))
      end
    when arity == 0
      proc { |*args| self.call }
    when arity < 0
      rarity = -1 - arity
      proc do |*args|
        (rarity - args.size).times() { args << nil } if rarity > args.size
        self.call(*args)
      end
    end
  end
end

class A
  def talk
    puts "A#talk"
  end

  def var_arg(a,b,c,*others)
    p a,b,c, others
  end

  def normal(a,b,c)
    p a,b,c
  end
end

methproc = A.new.method(:talk).my_to_proc
m2 = A.new.method(:var_arg).my_to_proc
m3 = A.new.method(:normal).my_to_proc
[1].each(&methproc)
[1].each(&m2)
[1].each(&m3)


A#talk
1
nil
nil
[]
1
nil
nil

--
 _           _                            
| |__   __ _| |_ ___ _ __ ___   __ _ _ __  
| '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \
| |_) | (_| | |_\__ \ | | | | | (_| | | | |
|_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_|
        Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Save yourself from the 'Gates' of hell, use Linux."  -- like that one.



Tue, 09 Aug 2005 03:41:11 GMT  
 proc {} vs. Method#to_proc

Quote:




> > > >   pr = Proc.new { puts "hi from anonymous Proc" }
> > > >   methproc = A.new.method(:talk).to_proc

> > > >   [1,2,3].each &pr         # "hi from anonymous Proc\n" * 3
> > > >   [1,2,3].each &methproc   # in `talk': wrong # of arguments(1 for 0)

> > > Interesting. I would have just done

> > >   [1,2,3].each { methproc.call }

> > > and never have noticed. Can you explain what is happening when
> > > I replace {...} with &pr.

> > & turns a proc into a block.

> So why the error wrong # of arguments?

It seems that Method#to_proc works like

class Method
  def my_to_proc
    proc { |*args| self.call(*args) }
  end
end

no matter what you pass the block, it won't complain, but at some point
you actually call the method...

In eval.c you have

static VALUE
bmcall(args, method)
    VALUE args, method;
{
    args = svalue_to_avalue(args);
    return method_call(RARRAY(args)->len, RARRAY(args)->ptr, method);
  /* => this is the line responsible for the arity error if I'm right */

Quote:
}

static VALUE
method_proc(method)
    VALUE method;
{
    return rb_iterate((VALUE(*)_((VALUE)))mproc, 0, bmcall, method);

Quote:
}

Read my other msg. for a quick "solution".

--
 _           _                            
| |__   __ _| |_ ___ _ __ ___   __ _ _ __  
| '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \
| |_) | (_| | |_\__ \ | | | | | (_| | | | |
|_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_|
        Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

LILO, you've got me on my knees!

Dominos, and Werner Almsberger



Tue, 09 Aug 2005 03:52:37 GMT  
 proc {} vs. Method#to_proc
Hi --

Quote:


> > Hi --

> > Arising from a recent discussion on #ruby-lang:

> > Proc objects created by Method#to_proc seem to care about their arity
> > in a way that other Proc objects don't.  To illustrate:

> Procs don't care about the arity when they are transformed into blocks
> with '&': unneeded arguments are discarded and if there's not enough
> they get filled with nil.

But that isn't what's happening with the to_proc procs.  That's why
their behavior strikes me as anomalous.

David

--
David Alan Black


Web:  http://pirate.shu.edu/~blackdav



Tue, 09 Aug 2005 05:03:38 GMT  
 proc {} vs. Method#to_proc
Hi,

In message "proc {} vs. Method#to_proc"

|Proc objects created by Method#to_proc seem to care about their arity
|in a way that other Proc objects don't.

Method#to_proc returns a Proc defined like

  Proc{|*args| self.call(*args)}

this explains the behavior.

                                                        matz.



Tue, 09 Aug 2005 05:17:47 GMT  
 proc {} vs. Method#to_proc



Quote:
> Hi,

> In message "proc {} vs. Method#to_proc"

writes:

> |Proc objects created by Method#to_proc seem to care about their arity
> |in a way that other Proc objects don't.

> Method#to_proc returns a Proc defined like

>   Proc{|*args| self.call(*args)}

> this explains the behavior.

While we're at it...  I did a little testing and two things strike me odd:

irb(main):143:0* RUBY_VERSION
=> "1.6.8"
irb(main):144:0> procs = [
irb(main):145:1*   Proc.new{|a| p a},
irb(main):146:1*   Proc.new{|*a| p a},
irb(main):147:1*   Proc.new{|a,| p a},
irb(main):148:1*   Proc.new{|a,b| p a}
irb(main):149:1> ]
=> [#<Proc:0x27dda90>, #<Proc:0x27dda60>, #<Proc:0x27dda30>,
#<Proc:0x27dda00>]
irb(main):150:0> procs.each_with_index{|p,i| puts "proc[#{i}].arity =
#{p.arity}
"}
proc[0].arity = -1
proc[1].arity = -1
proc[2].arity = 1
proc[3].arity = 2
=> [#<Proc:0x27dda90>, #<Proc:0x27dda60>, #<Proc:0x27dda30>,
#<Proc:0x27dda00>]
irb(main):151:0> procs.each_with_index{|p,i| puts "proc[#{i}]"
irb(main):152:1>   begin
irb(main):153:2*     p.call(i)
irb(main):154:2>   rescue
irb(main):155:2>     puts "ERROR"
irb(main):156:2>   end
irb(main):157:1> }
proc[0]
0
proc[1]
[1]
proc[2]
2
proc[3]
ERROR
=> [#<Proc:0x27dda90>, #<Proc:0x27dda60>, #<Proc:0x27dda30>,
#<Proc:0x27dda00>]
irb(main):158:0>

1) Why is it that "proc[0].arity = -1"?  Or put differently: To me it seems
there is an asymmetry between proc[0].arity = proc[1].arity and the behavior
of proc[0] that resembles more p[2].  If "proc[0].arity = 1" I would be
fine, but -1 puzzles me...

2) Why does this parse "Proc.new{|a,| p a}"? (the comma with following bar)

Thanks!

    robert



Tue, 09 Aug 2005 06:25:00 GMT  
 proc {} vs. Method#to_proc

Quote:

> irb(main):144:0> procs = [
> irb(main):145:1*   Proc.new{|a| p a},
> irb(main):146:1*   Proc.new{|*a| p a},
> irb(main):147:1*   Proc.new{|a,| p a},
> irb(main):148:1*   Proc.new{|a,b| p a}
> irb(main):149:1> ]

> 1) Why is it that "proc[0].arity = -1"?  Or put differently: To me it seems
> there is an asymmetry between proc[0].arity = proc[1].arity and the behavior
> of proc[0] that resembles more p[2].  If "proc[0].arity = 1" I would be
> fine, but -1 puzzles me...

-1 means 0 or more args, -2 means 1 or more args, etc

Quote:
> 2) Why does this parse "Proc.new{|a,| p a}"? (the comma with following bar)

the |...| construct is pretty much the same as multiple assignment with
,

a,b,c = [1,2,3]

a, = [:a, :b, :c]

a is now :a, and :b and :c got thrown away.

You can even do stuff like:

class Foo
  attr_accessor :bar
end

f = Foo.new

[1,2,3].each do |f.bar|
end

f.bar == 3 #true

--

All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E  7C11 332A 551C 796C 9F04

  application_pgp-signature_part
< 1K Download


Tue, 09 Aug 2005 07:13:44 GMT  
 proc {} vs. Method#to_proc



Quote:

> > irb(main):144:0> procs = [
> > irb(main):145:1*   Proc.new{|a| p a},
> > irb(main):146:1*   Proc.new{|*a| p a},
> > irb(main):147:1*   Proc.new{|a,| p a},
> > irb(main):148:1*   Proc.new{|a,b| p a}
> > irb(main):149:1> ]

> > 1) Why is it that "proc[0].arity = -1"?  Or put differently: To me it
seems
> > there is an asymmetry between proc[0].arity = proc[1].arity and the
behavior
> > of proc[0] that resembles more p[2].  If "proc[0].arity = 1" I would be
> > fine, but -1 puzzles me...

> -1 means 0 or more args, -2 means 1 or more args, etc

I know that, sorry if I wasn't clear enough. What bugs me is the fact that
the block with a single argument has negative arity, indicating that there
can be more arguments. Why then doesn't the block with two arguments have
arity -2, too?  Or otherwise, why doesn't the block with |a| have arity 1?

Quote:
> > 2) Why does this parse "Proc.new{|a,| p a}"? (the comma with following
bar)

> the |...| construct is pretty much the same as multiple assignment with,

> a,b,c = [1,2,3]

> a, = [:a, :b, :c]

It just seemed strange to me that you can have a comma here but no
following enumerated element - especially since a block definition with
|a,| behaves differently from the one with |a|.

Regards

    robert



Tue, 09 Aug 2005 15:50:33 GMT  
 proc {} vs. Method#to_proc

Quote:

> Hi --



> > > Hi --

> > > Arising from a recent discussion on #ruby-lang:

> > > Proc objects created by Method#to_proc seem to care about their arity
> > > in a way that other Proc objects don't.  To illustrate:

> > Procs don't care about the arity when they are transformed into blocks
> > with '&': unneeded arguments are discarded and if there's not enough
> > they get filled with nil.

> But that isn't what's happening with the to_proc procs.  That's why
> their behavior strikes me as anomalous.

Read the rest of my message :)

to_proc works like
 proc { |*args| self.call(*args) }
calling the block works (it accepts any number of args, so even .call
would), self.call fails.

You can use #my_to_proc in [ruby-talk:65372] to make these procs behave like the
others.

--
 _           _                            
| |__   __ _| |_ ___ _ __ ___   __ _ _ __  
| '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \
| |_) | (_| | |_\__ \ | | | | | (_| | | | |
|_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_|
        Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

* liw prefers not to have Linus run Debian, because then /me would
  have to run Red Hat, just to keep the power balance :)
        -- #Debian



Tue, 09 Aug 2005 16:05:00 GMT  
 proc {} vs. Method#to_proc
Hi,

In message "Re: proc {} vs. Method#to_proc"

|> -1 means 0 or more args, -2 means 1 or more args, etc
|
|I know that, sorry if I wasn't clear enough. What bugs me is the fact that
|the block with a single argument has negative arity, indicating that there
|can be more arguments. Why then doesn't the block with two arguments have
|arity -2, too?  Or otherwise, why doesn't the block with |a| have arity 1?

  proc{|a|...}.call(1,2,3)

works like

  a = 1,2,3   # a = [1,2,3]

so that it takes arbitrary number of argument (thus arity = -1).  If
you want to receive one argument and only one, you have to do |a,|
which works like

  a, = 1,2,3  # a = 1

                                                        matz.



Tue, 09 Aug 2005 16:22:50 GMT  
 proc {} vs. Method#to_proc

Quote:

> I know that, sorry if I wasn't clear enough. What bugs me is the fact that
> the block with a single argument has negative arity, indicating that there
> can be more arguments. Why then doesn't the block with two arguments have
> arity -2, too?  Or otherwise, why doesn't the block with |a| have arity 1?

I think it has to do with being dynamic so that iterators and blocks can
be combined more easily.

The fact is that with one block argument, if more values are yielded to
it, they are collected in an array and passed in the one argument. Thus
-1 arity. With two block parameters, where would the extra yielded
values go? We could have treated the last parameters as *args in
methods, but then too much is going on at once for me atleast. (And
getting used to blocks took a while for me.)

For a two argument -2 arity block:
  {|a,*b|}

It might not seem apparent at first, but the special case of the lone
block parameter helps make Ruby more dynamic, IMHO. Iterating over
hashes is a rather obvious case:
  {1=>3, 2=>4}.each{|a,b| puts "#{a}->#{b}"}
versus
  {1=>3, 2=>4}.each{|a| puts "#{a.first}->#{a.last}"}

With the latter block, you could have your iterator change a lot more
without affecting your usage of it. You, the block writer, do not have
to worry about whether the iterator yields one value (an array with two
values) or two separate values. And that is yet another bit of freedom
and dynamicity that makes Ruby stand out. (This kind of magic is
inherent in return values too, IIRC)

Quote:
> It just seemed strange to me that you can have a comma here but no
> following enumerated element - especially since a block definition with
> |a,| behaves differently from the one with |a|.

An extra character and visual complexity for those who are really strict
about what they'll accept from the iterator and bind themselves closer
to the iterator implementation. Suits them right :-)

--
(\[ Kent Dahl ]/)_    _~_    __[ http://www.stud.ntnu.no/~kentda/ ]___/~
 ))\_student_/((  \__d L b__/  NTNU - graduate engineering - 5. year  )
( \__\_?|?_/__/ ) _)Industrial economics and technological management(
 \____/_?_\____/ (____engineering.discipline_=_Computer::Technology___)



Tue, 09 Aug 2005 16:32:23 GMT  
 proc {} vs. Method#to_proc

Hi Kent,

thanks for the lengthy explanation.  Some remarks below.



Quote:

> > I know that, sorry if I wasn't clear enough. What bugs me is the fact
that
> > the block with a single argument has negative arity, indicating that
there
> > can be more arguments. Why then doesn't the block with two arguments
have
> > arity -2, too?  Or otherwise, why doesn't the block with |a| have arity
1?

> I think it has to do with being dynamic so that iterators and blocks can
> be combined more easily.

> The fact is that with one block argument, if more values are yielded to
> it, they are collected in an array and passed in the one argument. Thus
> -1 arity. With two block parameters, where would the extra yielded
> values go? We could have treated the last parameters as *args in
> methods, but then too much is going on at once for me atleast. (And
> getting used to blocks took a while for me.)

> For a two argument -2 arity block:
>   {|a,*b|}

> It might not seem apparent at first, but the special case of the lone
> block parameter helps make Ruby more dynamic, IMHO. Iterating over
> hashes is a rather obvious case:
>   {1=>3, 2=>4}.each{|a,b| puts "#{a}->#{b}"}
> versus
>   {1=>3, 2=>4}.each{|a| puts "#{a.first}->#{a.last}"}

> With the latter block, you could have your iterator change a lot more
> without affecting your usage of it. You, the block writer, do not have
> to worry about whether the iterator yields one value (an array with two
> values) or two separate values. And that is yet another bit of freedom
> and dynamicity that makes Ruby stand out. (This kind of magic is
> inherent in return values too, IIRC)

I'm not fully convinced. The reason is, that unless the parameters are just
passed on to some other processing, the block writer must expect multiple
values. In you example, writing

{1=>3, 2=>4}.each{|*a| puts "#{a.first}->#{a.last}"}   # note the "*a"

is not too much overhead but a bit clearer IMHO.  If you write a block that
relies on the some type property (i.e. method present) you have to
explicitely deal with that:

X.each do |a|
  if a.kind_of? String then
    p a if a =~ /foo/
  else
    a.each do |e|
      p e if e =~ /foo/
    end
  end
end

or

X.each do |a|
  a.to_a.each do |e|
    p e if e =~ /foo/
  end
end

or

X.each do |*a|
  a.each do |e|
    p e if e =~ /foo/
  end
end

But I guess it's a lot a matter of taste.

Quote:
> > It just seemed strange to me that you can have a comma here but no
> > following enumerated element - especially since a block definition with
> > |a,| behaves differently from the one with |a|.

> An extra character and visual complexity for those who are really strict
> about what they'll accept from the iterator and bind themselves closer
> to the iterator implementation. Suits them right :-)

:-)

Regards

    robert



Tue, 09 Aug 2005 17:12:32 GMT  
 
 [ 22 post ]  Go to page: [1] [2]

 Relevant Pages 

1. Class methods vs Instance methods

2. Class method vs instance method???

3. Class method vs instance method???

4. IncrTcl: calling static methods vs instance methods

5. parser API style - methods take path or file vs sep methods for path and file

6. multiple blocks or proc arguments to method

7. Method <=> Proc

8. module_eval: adding a method to a class with a proc

9. Proc/Method objects are great

10. Method and Proc equality

11. PLS-violation in proc as block method invocation!?

12. Giving a Proc utility methods?

 

 
Powered by phpBB® Forum Software