Help with a ruby idiom

T

Tim Waters

From the O'Reilly Cookbook there is code that keeps call functions in a
hash as a subscriber/listener type pattern. What I don't understand (or
I don't understand how it works is the line:
(@EventDispatcher_listeners[event] ||= []) << callback


module EventDispatcher
def setup_listeners
@EventDispatcher_listeners = {}
end

def subscribe(event, &callback)
(@EventDispatcher_listeners[event] ||= []) << callback
end

protected
def notify(event, *args)
if @EventDispatcher_listeners[event]
@EventDispatcher_listeners[event].each do |m|
m.call(*args) if m.respond_to? :call
end
end
return nil
end
end
 
V

Vincent Fourmond

Tim said:
From the O'Reilly Cookbook there is code that keeps call functions in a
hash as a subscriber/listener type pattern. What I don't understand (or
I don't understand how it works is the line:
(@EventDispatcher_listeners[event] ||= []) << callback

You can split it into two, which will make it more clear:

@EventDispatcher_listeners[event] ||= [] # makes it an empty array if
it doesn't exist
@EventDispatcher_listeners[event] << callback # and pushes the callback
on top of it

I agree that this kind of compact notation is very hand but slightly
indecipherable... Cheers,

Vince
 
R

Robert Klemme

From the O'Reilly Cookbook there is code that keeps call functions in a
hash as a subscriber/listener type pattern. What I don't understand (or
I don't understand how it works is the line:
(@EventDispatcher_listeners[event] ||= []) << callback

a ||= b

is equivalent to

a || (a = b)

IOW

a = b unless a

IOW

unless a
a = b
end

IOW

"eval b and assign the result to a if a is nil or false".

In this case, since a Hash is also involved the lengthy form looks like
this:

unless @EventDispatcher_listeners[event]
@EventDispatcher_listeners[event] = []
end
@EventDispatcher_listeners[event] << callback

I hope you admit that ||= is much more concise and elegant. :)

HTH

robert
 
M

matt neuburg

Tim Waters said:
What I don't understand (or
I don't understand how it works is the line:
(@EventDispatcher_listeners[event] ||= []) << callback

Here is a verbose a description:

@EventDispatcher_listeners is a hash.

Look up the entry in that hash, the entry named "event".

But perhaps there is no such entry. (The entry is reported as nil.) In
that case, create one, and set it to be an empty array.

Now you have an array - either the array that was already the "event"
entry in the hash, or the empty array that is now the "event" entry in
the hash. Append "callback" as the last item of that array.

m.
 
T

Tim Waters

Robert said:
I hope you admit that ||= is much more concise and elegant. :)

HTH

robert

Thanks to all, I think I understand now. And yes, it is QUITE concise!
-tim
 
D

dblack

Hi --

From the O'Reilly Cookbook there is code that keeps call functions in a
hash as a subscriber/listener type pattern. What I don't understand (or
I don't understand how it works is the line:
(@EventDispatcher_listeners[event] ||= []) << callback

a ||= b

is equivalent to

a || (a = b)

IOW

a = b unless a

IOW

unless a
a = b
end

IOW

"eval b and assign the result to a if a is nil or false".

It is indeed functionally equivalent to those, though all of them
would blow up :) I believe the reason a ||= b doesn't blow up is
that it's expanded to:

a = a || b

and when Ruby sees "a =" it allocates a local variable called a -- so
that means that the rhs will not raise an error, which it otherwise
would.

This doesn't add anything to what the idiom does, but I think it's an
interesting semantic detail.


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
D

Devin Mullins

It is indeed functionally equivalent to those, though all of them
would blow up :)
Not all of them.
a = b unless a
works, for the same reason you described
a = a || b
working. Here, the "a =" is parsed before the "unless a" is evaluated.

But yes, a ||= b maps more closely to a = a || b, just like a += b maps
to a = a + b.

Devin
(Though, I tend to use the ||= idiom most often with instance/class
variables, who don't raise NameError like locals do.)
 
D

dblack

Hi --

Not all of them.
a = b unless a
works, for the same reason you described
a = a || b
working. Here, the "a =" is parsed before the "unless a" is evaluated.

Whoops, right; thanks.


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
M

M. Edward (Ed) Borasky

Hi --



Whoops, right; thanks.


David
Might I be a curmudgeon on this Christmas eve eve? I say that any idiom
that's confusing enough that its semantics isn't *instantly*
recognizable to an old FORTRAN programmer like my curmudgeonly self
probably doesn't contribute to code readability. :) So I would write
this out in "longhand" and not let the parser have an opportunity to
confuse me. So my "idiom" would be

if (!a) then
a = b
end

Or, since my curmudgeonly self has managed to learn Perl, :)

a = b if !a
 
D

dblack

Hi --

Might I be a curmudgeon on this Christmas eve eve? I say that any idiom
that's confusing enough that its semantics isn't *instantly* recognizable to
an old FORTRAN programmer like my curmudgeonly self probably doesn't
contribute to code readability. :) So I would write this out in "longhand"
and not let the parser have an opportunity to confuse me. So my "idiom" would
be

if (!a) then
a = b
end

That won't work though:

ruby -e 'if (!a) then a = b end'
-e:1: undefined local variable or method `a' for main:Object
(NameError)

because of the forward reference to a.
Or, since my curmudgeonly self has managed to learn Perl, :)

a = b if !a

||= is used a lot in Perl, too :) I guess I'm just used to it; my
eye takes it in pretty readily.


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
W

William James

M. Edward (Ed) Borasky said:
Might I be a curmudgeon on this Christmas eve eve? I say that any idiom
that's confusing enough that its semantics isn't *instantly*
recognizable to an old FORTRAN programmer like my curmudgeonly self
probably doesn't contribute to code readability. :) So I would write
this out in "longhand" and not let the parser have an opportunity to
confuse me. So my "idiom" would be

if (!a) then
a = b
end

Or, since my curmudgeonly self has managed to learn Perl, :)

a = b if !a

a = b unless a
 
M

M. Edward (Ed) Borasky

Hi --



That won't work though:

ruby -e 'if (!a) then a = b end'
-e:1: undefined local variable or method `a' for main:Object
(NameError)

because of the forward reference to a.


||= is used a lot in Perl, too :) I guess I'm just used to it; my
eye takes it in pretty readily.


David
Does this Perl-ism work?

if (!defined(a)) then
a = b
end
 
D

dblack

Hi --

Does this Perl-ism work?

if (!defined(a)) then
a = b
end

Yes, but in Ruby it's defined? rather than defined.


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
G

gwtmp01

Isn't an idiom by definition somewhat unique to its cultural/language
context? If an expression is instantly recognizable by non-native
speakers of the language then it isn't an idiom at all.

Gary Wright
 
Z

znmeb

Quoting (e-mail address removed):
Isn't an idiom by definition somewhat unique to its cultural/language
context? If an expression is instantly recognizable by non-native
speakers of the language then it isn't an idiom at all.

Gary Wright

Uh ... OK ... I'll be the curmudgeon and you can be the pedant. :)

Seriously though, you're right. As David Black pointed out, it's more or less
inherited from Perl, as is "unless". I find "unless" difficult to read in many
circumstances, although it does seem natural in this one:

a = b unless defined?(a)

the missing word "already" gets filled in by my mind.

a ||= b

on the other hand, doesn't read as easily to me.
 
R

Robert Klemme

Hi --

From the O'Reilly Cookbook there is code that keeps call functions in a
hash as a subscriber/listener type pattern. What I don't understand (or
I don't understand how it works is the line:
(@EventDispatcher_listeners[event] ||= []) << callback

a ||= b

is equivalent to

a || (a = b)

IOW

a = b unless a

IOW

unless a
a = b
end

IOW

"eval b and assign the result to a if a is nil or false".

It is indeed functionally equivalent to those, though all of them
would blow up :) I believe the reason a ||= b doesn't blow up is
that it's expanded to:

a = a || b

and when Ruby sees "a =" it allocates a local variable called a -- so
that means that the rhs will not raise an error, which it otherwise
would.

This doesn't add anything to what the idiom does, but I think it's an
interesting semantic detail.

Absolutely! I had this slightly nagging feeling that I had the first
variant wrong, and indeed

$ ruby -e 'a || (a=1); p a'
-e:1: undefined local variable or method `a' for main:Object (NameError)

Thanks for the correction!

robert
 
D

David Vallner

--------------enig80FCAF12A4299B33D712994D
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

=20
||=3D is used a lot in Perl, too :) I guess I'm just used to it; my
eye takes it in pretty readily.
=20

With no Perl background whatsoever, and a chronic dislike of things
terse / golfy, I'll admit to using and liking ||=3D. Mostly because it
maps to a single simple concept - I don't even begin to read it as
"assign b to a *if* a doesn't exist", I mentally parse it as "the lazy
initialisation operator" as a unit.

David Vallner


--------------enig80FCAF12A4299B33D712994D
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (MingW32)

iD8DBQFFkAKFy6MhrS8astoRAiiyAJ0fOHR5npPZFh924JZxtjWCIzMchACfTnn1
Zc1Ic40zmeOxkJU6+vLZ8p8=
=ulED
-----END PGP SIGNATURE-----

--------------enig80FCAF12A4299B33D712994D--
 
W

Wayne Vucenic

Hi Devin,

(Though, I tend to use the ||= idiom most often with instance/class
variables, who don't raise NameError like locals do.)

Not to be picky, but class variables do raise NameError:

C:\>ruby -e "puts @@a"
-e:1: uninitialized class variable @@a in Object (NameError)

C:\>ruby -v
ruby 1.8.2 (2004-12-25) [i386-mswin32]

Personally, the way I remember this is that if there's an even
number of prefix characters before the variable name
(like a or @@a) it raises NameError, but if there's an odd
number (like @a or $a), it doesn't.

Merry Christmas!

Wayne
 

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

Forum statistics

Threads
474,221
Messages
2,571,134
Members
47,748
Latest member
LyleMondra

Latest Threads

Top