Question about closure example on p. 56 of pickaxe book

A

Alex Strasheim

I'm new to both Ruby and closures, and I'm trying to nail down exactly
what's going on in an example on p. 56 in the 2nd edition of
Programming Ruby:

songlist = SongList.new

class JukeboxButton < Button

def initialize(label, &action)
super(label)
@action = action
end

def button_pressed
@action.call(self)
end

end

start_button = JukeboxButton.new("Start") { songlist.start }
pause_button = JukeboxButton.new("Pause") {songlist.pause }

(I typed this in manually, so if there are typos, they come from me,
and not the book.)

I understand that @action is an instance variable that points to a
Proc object containing the block of code in the curly brackets. And I
think I understand the idea that the context of the block is saved
along with the code in the Proc object.

What I don't understand is why we need the parameter "self" in:

@action.call(self)

When someone presses the start button, that generates a call to
start_button.button_pressed, right? So when we run that method, we do
@action.call, which would songlist.start to run.

Why do we need "self"? Doesn't "self" refer to start_button, which is
an instance of JukeboxButton? Why would songlist need to know about
the button?

And if we were going to pass a parameter when we run @action.call,
wouldn't the block of code need to have a parameter in it? Like this:

start_button = JukeboxButton.new("Start") { |x| songlist.start }

I realize that doesn't make sense -- the parameter isn't used in that
code block. But I'm pretty confused by what's going on here.

Thanks (and I hope this isn't too dumb, or inappropriate.)
 
M

Michael Fellinger

I'm new to both Ruby and closures, and I'm trying to nail down exactly
what's going on in an example on p. 56 in the 2nd edition of
Programming Ruby:

songlist = SongList.new

class JukeboxButton < Button

def initialize(label, &action)
super(label)
@action = action
end

def button_pressed
@action.call(self)
end

end

start_button = JukeboxButton.new("Start") { songlist.start }
pause_button = JukeboxButton.new("Pause") {songlist.pause }

(I typed this in manually, so if there are typos, they come from me,
and not the book.)

I understand that @action is an instance variable that points to a
Proc object containing the block of code in the curly brackets. And I
think I understand the idea that the context of the block is saved
along with the code in the Proc object.

What I don't understand is why we need the parameter "self" in:

@action.call(self)

When someone presses the start button, that generates a call to
start_button.button_pressed, right? So when we run that method, we do
@action.call, which would songlist.start to run.

Why do we need "self"? Doesn't "self" refer to start_button, which is
an instance of JukeboxButton? Why would songlist need to know about
the button?

And if we were going to pass a parameter when we run @action.call,
wouldn't the block of code need to have a parameter in it? Like this:

start_button = JukeboxButton.new("Start") { |x| songlist.start }

I realize that doesn't make sense -- the parameter isn't used in that
code block. But I'm pretty confused by what's going on here.

Thanks (and I hope this isn't too dumb, or inappropriate.)

Your question is understandable, and it's a bit odd that they pass
self as an parameter since it's not used at all.
My advice: ignore it.
Might be something that slipped through the final review or something.
There is no hidden meaning and it works perfectly without passing
self.

Hope that clears things up,
^ manveru
 
P

Phrogz

And if we were going to pass a parameter when we run @action.call,
wouldn't the block of code need to have a parameter in it? Like this:

start_button = JukeboxButton.new("Start") { |x| songlist.start }

Michael answered the brunt of your question as I would have ("ignore
the self"), but I wanted to answer the above question of yours.

While methods in Ruby are pretty adamant about getting exactly the
number of parameters you define (except when you explicitly provide
default values or explicitly ask to collect any 'extra' values into an
array), blocks are much more forgiving. See the following code:

def block_to_proc( &blok )
blok
end

no_arg = block_to_proc{ 'foo' }
one_arg = block_to_proc{ |x| "foo-#{x.inspect}" }
only_one_used = block_to_proc{ |x,y| "foo-#{x.inspect}" }
two_arg_used = block_to_proc{ |x,y| "foo-#{x.inspect}-
#{y.inspect}" }

puts no_arg.call
#=> foo

puts no_arg.call( 'bar' )
#=> foo

puts one_arg.call
#=> warning: multiple values for a block parameter (0 for 1)
#=> foo-nil

puts one_arg.call( 'bar' )
#=> foo-"bar"

puts one_arg.call( 'bar', 'jim' )
#=> foo-["bar", "jim"]
#=> warning: multiple values for a block parameter (2 for 1)

puts only_one_used.call
#=> foo-nil

puts only_one_used.call( 'bar' )
#=> foo-"bar"

puts only_one_used.call( 'bar', 'jim' )
#=> foo-"bar"

puts only_one_used.call( 'bar', 'jim', 'jam' )
#=> foo-"bar"

puts two_arg_used.call
#=> foo-nil-nil

puts two_arg_used.call( 'bar' )
#=> foo-"bar"-nil

puts two_arg_used.call( 'bar', 'jim' )
#=> foo-"bar"-"jim"

puts two_arg_used.call( 'bar', 'jim', 'jam' )
#=> foo-"bar"-"jim"

Note that the above behaves the same if you use Proc.new instead of
the block_to_proc function. In 1.8.6, if you use 'proc' or 'lambda'
instead, the no_arg and one_arg examples work the same, but you get an
ArgumentError ("wrong number of arguments (0 for 2)") raised and
halting your program when you try to call only_one_used without any
arguments. (And if you comment that out, you also get that same error
when you try to call it with too many arguments.)

I provide the above without any justification for the behavior. :)
This is with Ruby 1.8.6.

My own question: what rationale allows the one-argument Proc 'auto-
splat' the extra arguments into an array, but the two-argument case
does not? And what changes in 1.9 (besides 'proc' becoming another way
of performing 'Proc.new' instead of 'lambda')?
 
A

Alex Strasheim

Your question is understandable, and it's a bit odd that they pass
self as an parameter since it's not used at all.
My advice: ignore it.
Might be something that slipped through the final review or something.
There is no hidden meaning and it works perfectly without passing
self.

Hope that clears things up,

It does... I was afraid it was some sort of strange Proc magic that I
didn't understand.

Thanks very much
 
A

Alex Strasheim

Michael answered the brunt of your question as I would have ("ignore
the self"), but I wanted to answer the above question of yours.

While methods in Ruby are pretty adamant about getting exactly the
number of parameters you define (except when you explicitly provide
default values or explicitly ask to collect any 'extra' values into an
array), blocks are much more forgiving. See the following code:

[...]

Thanks very much for taking the time to type that out. It's really
helpful.
My own question: what rationale allows the one-argument Proc 'auto-
splat' the extra arguments into an array, but the two-argument case
does not? And what changes in 1.9 (besides 'proc' becoming another way
of performing 'Proc.new' instead of 'lambda')?

I'll be curious to read what's said about that as well.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top