Help with a ruby idiom


#1

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


#2

Tim W. wrote:

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

#3

On 23.12.2006 17:02, Tim W. wrote:

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. :slight_smile:

HTH

robert

#4

Robert K. wrote:

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

HTH

robert

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


#5

Tim W. removed_email_address@domain.invalid wrote:

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.


#6

Hi –

On Sun, 24 Dec 2006, Robert K. wrote:

a || (a = b)

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 :slight_smile: 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


#7

Hi –

On Sun, 24 Dec 2006, Devin M. wrote:

removed_email_address@domain.invalid wrote:

It is indeed functionally equivalent to those, though all of them
would blow up :slight_smile:
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


#8

removed_email_address@domain.invalid wrote:

It is indeed functionally equivalent to those, though all of them
would blow up :slight_smile:
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.)


#9

removed_email_address@domain.invalid wrote:

a = a || b
working. Here, the “a =” is parsed before the “unless a” is evaluated.

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. :slight_smile: 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, :slight_smile:

a = b if !a


M. Edward (Ed) Borasky, FBG, AB, PTA, PGS, MS, MNLP, NST, ACMC§
http://borasky-research.blogspot.com/

If God had meant for carrots to be eaten cooked, He would have given
rabbits fire.


#10

Hi –

On Sun, 24 Dec 2006, M. Edward (Ed) Borasky wrote:

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. :slight_smile: 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, :slight_smile:

a = b if !a

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

David


#11

M. Edward (Ed) Borasky wrote:

works, for the same reason you described
recognizable to an old FORTRAN programmer like my curmudgeonly self
a = b if !a
a = b unless a


#12

Hi –

On Sun, 24 Dec 2006, M. Edward (Ed) Borasky wrote:

would be

David

Does this Perl-ism work?

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

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

David


#13

On Dec 23, 2006, at 3:35 PM, William J. wrote:

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. :slight_smile:

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 W.


#14

Quoting removed_email_address@domain.invalid:

speakers of the language then it isn’t an idiom at all.

Gary W.

Uh … OK … I’ll be the curmudgeon and you can be the pedant. :slight_smile:

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.


#15

removed_email_address@domain.invalid wrote:

because of the forward reference to a.

Does this Perl-ism work?

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


M. Edward (Ed) Borasky, FBG, AB, PTA, PGS, MS, MNLP, NST, ACMC§
http://borasky-research.blogspot.com/

If God had meant for carrots to be eaten cooked, He would have given
rabbits fire.


#16

On 23.12.2006 19:55, removed_email_address@domain.invalid wrote:

a ||= b

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

#17

removed_email_address@domain.invalid wrote:

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

a = b if !a

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

With no Perl background whatsoever, and a chronic dislike of things
terse / golfy, I’ll admit to using and liking ||=. 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 V.


#18

Hi Devin,

On 12/23/06, Devin M. removed_email_address@domain.invalid wrote:

(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


Wayne V.
No Bugs Software
Ruby, C# and Erlang Agile Contract Programming in Silicon Valley