On Fri, Feb 21, 2003 at 01:19:17AM +0900, dblack / candle.superlink.net wrote:
> 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.
 
>> p = proc { |a,b,c,d,e| p(a) }
=> #<Proc:0x402ab4b4>
>> [1,2,3].each &p
(irb):4: warning: `&' interpreted as argument prefix
1
2
3
=> [1, 2, 3]
>> p.call(1)
ArgumentError: wrong # of arguments (1 for 5)
        from (irb):5
        from (irb):3:in `call'
        from (irb):5

>   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:
batsman@tux-chan:/tmp$ expand -t 2 j.rb
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)

batsman@tux-chan:/tmp$ ruby j.rb
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:

batsman@tux-chan:/tmp$ expand -t 2 k.rb

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)

batsman@tux-chan:/tmp$ ruby k.rb
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.
	-- The_Kind @ LinuxNet