Forum: Ruby Help with a ruby idiom

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
16b25a8544e04f6ae2315837a9c615d4?d=identicon&s=25 Tim Waters (twaters)
on 2006-12-23 17:02
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
Ea24c17719a975fb38c107a60f4b3802?d=identicon&s=25 Vincent Fourmond (Guest)
on 2006-12-23 17:09
(Received via mailing list)
Tim Waters 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
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2006-12-23 17:17
(Received via mailing list)
On 23.12.2006 17:02, Tim Waters 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. :-)

HTH

	robert
Fbb4d027695dfdf76bf448b15d7e306a?d=identicon&s=25 unknown (Guest)
on 2006-12-23 17:17
(Received via mailing list)
Tim Waters <timgwaters@gmail.com> 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.
16b25a8544e04f6ae2315837a9c615d4?d=identicon&s=25 Tim Waters (twaters)
on 2006-12-23 17:38
Robert Klemme wrote:
> 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
1fba4539b6cafe2e60a2916fa184fc2f?d=identicon&s=25 unknown (Guest)
on 2006-12-23 19:55
(Received via mailing list)
Hi --

On Sun, 24 Dec 2006, Robert Klemme 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 :-)  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
918c6daad03c85e51ad1a11f57017947?d=identicon&s=25 Devin Mullins (twifkak)
on 2006-12-23 20:03
(Received via mailing list)
dblack@wobblini.net wrote:
> 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.)
1fba4539b6cafe2e60a2916fa184fc2f?d=identicon&s=25 unknown (Guest)
on 2006-12-23 20:05
(Received via mailing list)
Hi --

On Sun, 24 Dec 2006, Devin Mullins wrote:

> dblack@wobblini.net wrote:
>> 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.

Whoops, right; thanks.


David
3bb23e7770680ea44a2d79e6d10daaed?d=identicon&s=25 M. Edward (Ed) Borasky (Guest)
on 2006-12-23 20:31
(Received via mailing list)
dblack@wobblini.net 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. :) 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



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

If God had meant for carrots to be eaten cooked, He would have given
rabbits fire.
1fba4539b6cafe2e60a2916fa184fc2f?d=identicon&s=25 unknown (Guest)
on 2006-12-23 21:23
(Received via mailing list)
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. :) 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
2ee1a7960cc761a6e92efb5000c0f2c9?d=identicon&s=25 William James (Guest)
on 2006-12-23 21:35
(Received via mailing list)
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
3bb23e7770680ea44a2d79e6d10daaed?d=identicon&s=25 M. Edward (Ed) Borasky (Guest)
on 2006-12-23 22:03
(Received via mailing list)
dblack@wobblini.net 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(P)
http://borasky-research.blogspot.com/

If God had meant for carrots to be eaten cooked, He would have given
rabbits fire.
1fba4539b6cafe2e60a2916fa184fc2f?d=identicon&s=25 unknown (Guest)
on 2006-12-23 22:19
(Received via mailing list)
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
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 unknown (Guest)
on 2006-12-24 03:54
(Received via mailing list)
On Dec 23, 2006, at 3:35 PM, William James 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. :)

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
3bb23e7770680ea44a2d79e6d10daaed?d=identicon&s=25 unknown (Guest)
on 2006-12-24 04:56
(Received via mailing list)
Quoting gwtmp01@mac.com:

> 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.
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2006-12-24 11:01
(Received via mailing list)
On 23.12.2006 19:55, dblack@wobblini.net 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
430ea1cba106cc65b7687d66e9df4f06?d=identicon&s=25 David Vallner (Guest)
on 2006-12-25 17:57
(Received via mailing list)
dblack@wobblini.net wrote:
>> 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.
>

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 Vallner
D83785463666ae9bb98a0753eebc8950?d=identicon&s=25 Wayne Vucenic (Guest)
on 2006-12-25 18:58
(Received via mailing list)
Hi Devin,

On 12/23/06, Devin Mullins <twifkak@comcast.net> 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 Vucenic
No Bugs Software
Ruby, C# and Erlang Agile Contract Programming in Silicon Valley
This topic is locked and can not be replied to.