Passing a named function instead of a code block?


#1

Hello,

I have a question about ruby’s feature that when you call a method, you
can pass in a block of code after the last argument. Instead of writing
a code block, suppose I already have a def’d method that would work just
as well. Is there any way I can pass that in directly? For example,
suppose I have this code:

#!/usr/bin/env ruby

def fib(n)
a, b = 0, 1
n.times do |i|
a, b = b, a+b
end
b
end

c = [1, 2, 3, 4]

puts c.collect {|i| fib i}

That will print fib(1), fib(2), fib(3), fib(4). But why write a code
block that takes one argument and does nothing but call a function that
takes one argument? Is there some way I could have replaced the last
line with something like this?:

puts c.collect \fib

In python I could have written the last line thus:

print map(fib, (1, 2, 3, 4))

Does ruby have something similar?

Thanks,
Paul


#2

Paul J. wrote:

I have a question about ruby’s feature that when you call a method, you
can pass in a block of code after the last argument. Instead of writing
a code block, suppose I already have a def’d method that would work just
as well. Is there any way I can pass that in directly? For example,
suppose I have this code:

#!/usr/bin/env ruby

def fib(n)
a, b = 0, 1
n.times do |i|
a, b = b, a+b
end
b
end

c = [1, 2, 3, 4]

puts c.collect {|i| fib i}

puts c.collect(&method(:fib))

-Matthias


#3

2009/3/20 7stud – removed_email_address@domain.invalid

(ArgumentError)
from r1test.rb:8

Why does ruby make you use the tortured syntax:

&method(:square1)

for a method vs. the easier syntax for a Proc object?

square2 is a variable name (ie. something you’ve made an assignment to),
it’s just a reference to the lambda object. However, square1 is a method
and
Ruby allows calling methods without parens, so ‘square1’ is actually
interpreted as a method call to square1 with no arguments. Therefore, to
grab a method as an object without calling it, we need to use
method(:square1).


#4

Matthias R. wrote:

puts c.collect(&method(:fib))

Why is there a difference here:

def square1(x)
x*x
end

square2 = lambda { |x| x*x}

puts [1, 2, 3].collect(&square2)
puts [1, 2, 3].collect(&square1)

–output:–
1
4
9
r1test.rb:8:in `square1’: wrong number of arguments (0 for 1)
(ArgumentError)
from r1test.rb:8

Why does ruby make you use the tortured syntax:

&method(:square1)

for a method vs. the easier syntax for a Proc object?


#5

James C. wrote:

2009/3/20 7stud – removed_email_address@domain.invalid

(ArgumentError)
from r1test.rb:8

Why does ruby make you use the tortured syntax:

&method(:square1)

for a method vs. the easier syntax for a Proc object?

square2 is a variable name (ie. something you’ve made an assignment to),
it’s just a reference to the lambda object. However, square1 is a method
and
Ruby allows calling methods without parens, so ‘square1’ is actually
interpreted as a method call to square1 with no arguments. Therefore, to
grab a method as an object without calling it, we need to use
method(:square1).

Ok. But there is a certain amount of hypocrisy in that explanation
Look here:

&square1
:square1

In the first expression there is a method call, and in the second there
isn’t. Yet, you could describe both those lines as: a method name
preceded by some symbol.


#6

2009/3/20 7stud – removed_email_address@domain.invalid

&method(:square1)
grab a method as an object without calling it, we need to use
preceded by some symbol.
Yes, it probably looks that way. To see the difference it helps to know
how
Ruby is parsed. :square1 is an atomic unit representing the symbol whose
name is ‘square1’. “:” is not an operator, it is part of the syntax for
symbols. However, “&” is an operator responsible for casting between
procs
and blocks. The expression ‘&square1’ should be read ‘&( square1 )’,
that is
we call square1 and cast the result of that using ‘&’. The same applies
to
‘method’. ‘method’ is a method that takes a symbol/string and returns
the
Method object with that name in the current scope. ‘method(square1)’
would
be interpreted as a call to square1, passing the result to ‘method’.

So, ‘&square1’ throws an error because you’re calling a method with
insufficient arguments. ‘&:square1’ would try to cast a symbol to a
proc,
which if you’re using ActiveSupport would return the block { |object|
object.square1 }.

‘&square2’ is fine as square2 is just a variable referring to a proc.
Likewise, ‘&method(:square1)’ is fine because method(:square1) is a
Method
object, which can be cast to a block.


#7

Okay, now how about when you want to reference a method on an instance?
For example:

#!/usr/bin/env ruby

class Adder
def initialize(n)
@n = n
end

def use(x)
    x + n
end

end

a = Adder.new(5)

print [1, 2, 3, 4].collect(&method(:a.use))

This doesn’t work. It looks like : binds tighter than … But :(a.use)
doesn’t work either. Any suggestions?


#8

Matthias R. wrote:

puts c.collect(&method(:fib))

Thank you for your help; this does just what I wanted.


#9

2009/3/20 Paul J. removed_email_address@domain.invalid

def use(x)
doesn’t work either. Any suggestions?
a.method(:use)

See docs for Object#method:
http://ruby-doc.org/core/classes/Object.html#M000338


#10

James C. wrote:

See docs for Object#method:
http://ruby-doc.org/core/classes/Object.html#M000338

Ah, so I want a line like this:

puts [1, 2, 3, 4].collect(&a.method(:use))

Actually, this helps me understand the previous solution. I guess
method(:foo) and a.method(:foo) are actually going to the same place,
huh? And each return a Method object, which I then cast to a block with
&. It is all starting to make sense…

Doesn’t this seem like an appropriate place for some syntactic sugar?
I’d be nice if you could abbreviate such calls with something like:
:foo
:a.foo
Since ruby is so focused on closures, this seems like an appropriate
place for such brevity.

Thanks again!


#11

James C. wrote:

2009/3/20 7stud – removed_email_address@domain.invalid

&method(:square1)
grab a method as an object without calling it, we need to use
preceded by some symbol.
Yes, it probably looks that way. To see the difference it helps to know
how
Ruby is parsed. :square1 is an atomic unit representing the symbol whose
name is ‘square1’. “:” is not an operator, it is part of the syntax for
symbols. However, “&” is an operator responsible for casting between
procs
and blocks. The expression ‘&square1’ should be read ‘&( square1 )’,
that is
we call square1 and cast the result of that using ‘&’. The same applies
to
‘method’. ‘method’ is a method that takes a symbol/string and returns
the
Method object with that name in the current scope. ‘method(square1)’
would
be interpreted as a call to square1, passing the result to ‘method’.

So, ‘&square1’ throws an error because you’re calling a method with
insufficient arguments. ‘&:square1’ would try to cast a symbol to a
proc,
which if you’re using ActiveSupport would return the block { |object|
object.square1 }.

‘&square2’ is fine as square2 is just a variable referring to a proc.
Likewise, ‘&method(:square1)’ is fine because method(:square1) is a
Method
object, which can be cast to a block.

Thanks for the explanation. You said that (&) casts between block and
Procs. But (&) also appears to be casting a Method to a block in this
line:

puts c.collect(&method(:fib))

(&) can also be used to cast in the reverse direction, e.g. from a block
to a Proc:

def test(&aProc)
aProc.call(10)
end

test {|x| puts x}

–output:–
10

Can (&) cast a block to a Method?


#12

On Mar 22, 2009, at 4:20 AM, 7stud – wrote:

to a Proc:

Can (&) cast a block to a Method?

There are no casts in Ruby.

& in a Method call calls #to_proc on an Object and expects it to
return a proc. Thats a conversion that has to be done explicitly by
the programmer.

& in a Method definition indicates that the argument has to be a Proc
or something that responds to #to_proc (or a passed Block) which then
gets enforced.

Blocks are Procs (AFAIK, internally, Methods are basically procs as
well). The only magic is “yield” together with a block, which to my
knowledge doesn’t require the construction of a Proc Object that has
to be visible to the programmer. But thats up to the implementation.

Regards,
Florian


Florian G.

smtp: removed_email_address@domain.invalid
jabber: removed_email_address@domain.invalid
gpg: 533148E2


#13

On 3/21/09, 7stud – removed_email_address@domain.invalid wrote:

symbols. However, “&” is an operator responsible for casting between

Thanks for the explanation. You said that (&) casts between block and
Procs. But (&) also appears to be casting a Method to a block in this
line:

puts c.collect(&method(:fib))

Actually, its casting a Method to a Proc (by calling to_proc on it)
and then treating the proc as a block. To see that’s what is
happening, try this:

class Integer
def to_proc
Proc.new { self }
end
end

(1…10).map(&1)

Can (&) cast a block to a Method?

No. & in a method call always treats a the given proc as a block given
to the method while & in a method definition always makes a block
given to the method as a Proc. Its probably best not to think of this
as “casting” except insofar as the first one “casts” to a proc first
by calling to_proc; blocks aren’t objects, and if you do this:

def consume(object=nil)
if block_given? then yield else object end
end

p = lambda { puts “foo!” }
consume(&p)

You don’t get the results you’ d get if an object of the
(non-existent) class “Block” was passed to consume, you get the
results you would get if consume was called with a block. So thinking
of & as “casting” an argument to a different class in the method call
is misleading.