# Pickaxe question: "... on the way to true Ruby mastery."

**URL:** <https://www.ruby-forum.com/t/pickaxe-question-on-the-way-to-true-ruby-mastery/81027>\
**Category:** Ruby\
**Created:** [January 3, 2007, 5:57am UTC](https://www.ruby-forum.com/t/pickaxe-question-on-the-way-to-true-ruby-mastery/81027 "2007-01-03T05:57:12Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 3, 2007, 5:57am UTC](https://www.ruby-forum.com/t/pickaxe-question-on-the-way-to-true-ruby-mastery/81027/1 "2007-01-03T05:57:12Z")

</div>

Pickaxe has this code on page 374:

def once(\*ids) # :nodoc:  
for id in ids  
module\_eval \<\<-“end;”  
alias\_method : **#{id.to\_i}** , :#{id.to\_s}  
private : **#{id.to\_i}**  
def #{id.to\_s}(\*args, &block)  
(@ **#{id.to\_i}** ||= [**#{id.to\_i}** (\*args, &block)])[0]  
end  
end;  
end  
end

followed by “Understand this code, and you’ll be well on the way to  
true Ruby mastery.” I don’t, so I’m not, but would like to, and then I  
will (according to the book) - with some help.

I’m good up until the line (@ **#{id.to\_i}** ||= [ **#{id.to\_i}** (\*args,  
&block)])[0]

Say the id.to\_i was 42 … then this reads: (@ **42** ||= [ **42** (\*args,  
&block)])[0]

or: (@ **42** ||= [return value from **42** method call] )[0]

Question: Why can’t we just write

(@ **42** ||= return value from **42** method call)

Why is the [0] necessary at all?

Thanks, (ps - a link to an in-depth explanation would be sufficient!)

Jeff

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 3, 2007, 6:29am UTC](https://www.ruby-forum.com/t/pickaxe-question-on-the-way-to-true-ruby-mastery/81027/2 "2007-01-03T06:29:15Z")

</div>

My guess is it is necessary to keep the value in the array in case the  
real  
value is nil. Without it in an array, @ **#{id.to\_i}** will evaluate to  
nil,  
and the || operator will execute the next term, the function call. You  
don’t  
want to call the function again, your desired value is already in the  
instance variable and it is nil.

(@ **#{id.to\_i}** ||= [**#{id.to\_i}** (\*args, &block)])[0]

-andre

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 3, 2007, 7:01am UTC](https://www.ruby-forum.com/t/pickaxe-question-on-the-way-to-true-ruby-mastery/81027/3 "2007-01-03T07:01:22Z")

</div>

Hey Andre, I believe the line reads slightly differently than you’ve  
interpreted … some more info (note: I still don’t have the answer):

||= is actually a ruby idiom to initialize a variable if it’s nil. In  
other words, “a ||= 6” actually means “a = 6 if a.nil?” or “a = 6  
unless a”. (sorry if I’m lecturing) In other words, if @foo really  
_is_ a value, and it really _is_ nil, I think the function call is  
going to be made no matter what … but again, this is a bit over my  
head at present.

So, converting **#{id.to\_i}** to something more readable, like “foo”,  
this: (@foo ||= [foo(\*a, &b)] )[0]

should be the same as the more verbose:

if @foo  
return @foo[0] # Still not sure why the [0] is required!  
else  
return foo( \*a, &b ) # the 0th index of the single array  
end

jz

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 3, 2007, 10:21am UTC](https://www.ruby-forum.com/t/pickaxe-question-on-the-way-to-true-ruby-mastery/81027/4 "2007-01-03T10:21:45Z")

</div>

Hi –

On Wed, 3 Jan 2007, removed\_email\_address@domain.invalid wrote:

> So, converting **#{id.to\_i}** to something more readable, like “foo”,  
> this: (@foo ||= [foo(\*a, &b)] )[0]
> 
> should be the same as the more verbose:
> 
> if @foo  
> return @foo[0] # Still not sure why the [0] is required!  
> else  
> return foo( \*a, &b ) # the 0th index of the single array  
> end

It’s more like:

if @foo  
return @foo[0]  
else  
@foo = [foo(\*a, &b)]  
return @foo[0]  
end

or, more simply,

@foo = [foo(\*a, &b)] unless @foo  
return @foo[0]

In other words, @foo gets assigned to, if it’s nil to start with.

David

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 3, 2007, 8:29am UTC](https://www.ruby-forum.com/t/pickaxe-question-on-the-way-to-true-ruby-mastery/81027/5 "2007-01-03T08:29:32Z")

</div>

Hi Jeff,

I think this is the intention of the code, that the assignment function  
is  
called precisely only once and it is generic, in a sense that it can  
store  
any value, including nil and ‘false’

Now, you are correct that what you describe there is the behavior of the  
code ‘to be replaced’ by ‘once’. The intention is the same, namely, the  
first call returned value should be cached and the subsequence call  
should  
just return that cached value, never to assigned again.

The original code, however, has drawback: if the cached value is nil or  
‘false’, the assignment will be made again. The ‘once’ code is an  
improvement over the original code that 1) it’s more elegant, 2) and it  
trully assigns only once, even with nil or ‘false’ cached value.

-andre

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 3, 2007, 8:12pm UTC](https://www.ruby-forum.com/t/pickaxe-question-on-the-way-to-true-ruby-mastery/81027/6 "2007-01-03T20:12:10Z")

</div>

Hi –

On Thu, 4 Jan 2007, removed\_email\_address@domain.invalid wrote:

> Thanks again. It’s pretty obvious, actually … sigh.
> 
> Funny, I still don’t feel well on my way to mastery. I actually feel  
> dumber, in a way.

I’d say Dave T.'s characterization of it as non-obvious and  
non-trivial is a good reality check in that regard 🙂 And I assure  
you that, having studied this example in depth, there’s a lot of Ruby  
code that you’ll take in stride now that you would have found opaque  
before. Since you’ll take it in stride, you may not _know_ that  
you’re taking it in stride, if you see what I mean – and that’s what  
keeps us eager to learn. (And “we” doesn’t mean just Ruby programmers  
or even just programmers, but people in general who are learning  
things.)

David

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 3, 2007, 7:05pm UTC](https://www.ruby-forum.com/t/pickaxe-question-on-the-way-to-true-ruby-mastery/81027/7 "2007-01-03T19:05:52Z")

</div>

Thanks David and Andre, I finally get it, and Andre, you’re right about  
the nil/false idea in your response to my reponse.

For anyone else following this thread, the idea is:

- The first time the method is called, @foo doesn’t exist.

- @foo doesn’t exist, so it’s assigned a single-element array  
containing the value returned by the call to the private aliased  
original method. This value can be anything, including an array,  
false, nil, whatever.

- The 0th array element, which is just the return value of the function  
call, is returned.

- The next time the method is called, @foo still exists, and is still a  
single-element array. We again return the 0th element, which is the  
answer.

The [0] is used because the result is stored in an array … this gets  
around the original method returning nil or false, which would end up  
in a second call to the function, had we just naively used @foo ||=  
foo( \*args, &block )…

Thanks again. It’s pretty obvious, actually … sigh.

Funny, I still don’t feel well on my way to mastery. I actually feel  
dumber, in a way.

jz

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 3, 2007, 11:56pm UTC](https://www.ruby-forum.com/t/pickaxe-question-on-the-way-to-true-ruby-mastery/81027/8 "2007-01-03T23:56:57Z")

</div>

removed\_email\_address@domain.invalid wrote:

> false, nil, whatever.  
> in a second call to the function, had we just naively used @foo ||=  
> foo( \*args, &block )…
> 
> Thanks again. It’s pretty obvious, actually … sigh.
> 
> Funny, I still don’t feel well on my way to mastery. I actually feel  
> dumber, in a way.
> 
> jz

Welcome to the wonderful world of meta-programming in Ruby! And don’t  
feel dumb. As the saying goes: the more you know, the more you know  
that you don’t know. Ask any true master of anything and that’ll be  
the answer they give you; if they’re a \>true\< master.

Ken

PS. Check these out to continue your mastery…

[http://www.erikveen.dds.nl/monitorfunctions/index.html](http://www.erikveen.dds.nl/monitorfunctions/index.html)  
[http://groups.google.com/group/comp.lang.ruby/browse\_thread/thread/22b97167fe614efc/2627d192d2bf56d5?hl=en#2627d192d2bf56d5](http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/22b97167fe614efc/2627d192d2bf56d5?hl=en#2627d192d2bf56d5)
