iBrasten

My methods of calculating time are far superior to yours, in every way.

 

This is the blog of Brasten Sager, a freelance software developer, Mariners fan, guitarist, haphazard philosopher.

We all do stupid things.

August 30, 2006 @ 06:39 AM

Earlier this month, Ben Bleything posted an article about something that annoyed and/or surprised him in Ruby. I suspect maybe Ben regrets posting that particular post, as a lot of people misunderstood what he was saying.

Along those lines, I ran into a particular feature of Ruby that has continually caused me grief. Hopefully I can preempt some of the comments Ben received by noting that I completely understand why the code does what it does, and I’m not saying it should be different; merely that I instinctually expect it to be different.

Enumerable#inject. For whatever reason I continually space-out on the fact that the memo object becomes the object returned by the block. This can manifest itself in my code as an inject method that conditionally modifies the memo object directly. If the conditions are not met, NOTHING is done, and all hell breaks loose.

Even with this incorrect understanding of Enumerable#inject, I can usually write inject calls that work as expected purely by accident. This just re-enforces my misunderstanding, leading to more stupid mistakes later. (Most of these are quickly caught, but…)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Dumb method, but a proper understanding of inject.  This works.
[1, 2, 3, 4, 5].inject { |memo, elem| memo + elem }
# => 15

# This is what I tend to do.  It also works, but you can easily see
# it works by accident, and I'm misunderstanding how inject works.
[1, 2, 3, 4, 5].inject { |memo, elem| memo = memo + elem }
# => 15

# This is where it first starts to show up.
# This particular case is caught very quickly.
[1, 2, 3, 4, 5].inject([]) { |memo, elem| memo << elem unless elem == 4 }
# => NoMethodError: undefined method `<<' for nil:NilClass


# Here's where I get into trouble.
# I'm expecting [1, 2, 3, 4].
# This code does not raise an error, but I'm
# left with a bogus value.
[1, 2, 3, 4, 5].inject([]) { |memo, elem| memo << elem unless elem == 5 }
# => nil

Anyone else do anything stupid like that? Leave a comment!

3 Responses to “We all do stupid things.”

  1. Yes! A couple of times until understanding inject properly... And also after that, when not using inject many times :)

    Eduardo Rocha

  2. Heh. I can't really grok inject for anything other than the simple (summation) case, so I don't really use it. It definitely _feels_ better to assign to memo all the time, as you do in your second example. And yeah, I would also anticipate the << methods working. Though I know they're contrived examples, 3 and 4 are why Matz gave us #reject :)

    Ben Bleythign

  3. "Though I know they’re contrived examples..." Yes. "...3 and 4 are why Matz gave us #reject" Yes. :)

    brasten