Check out the Ruby Gem at GitHub
Go ahead and ask the developer sitting next to you what is the thing that loves the most about Ruby. It should come as no surprise that simplicity, flexibility and expressiveness are the main reasons Ruby junkies just can’t get enough of it.
In Matz own words, seems like the ‘go with the flow’ philosophy was present right from the start in the Ruby community:
“Actually, I’m trying to make Ruby natural, not simple.”
Cuttin’ loose from the language cliché
I do believe in the principles and values Ruby was built upon, but I don’t like clichés. We should be critic about the restrictions a programming language impose on us, and really pay attention when things don’t feel right.
Here’s a concrete example to show you what I’m talking about. Check out these
uses of the
Do they ‘feel natural’ to you? Do you think there is something wrong with them? Take another look.
Noise & Duplication
There are two things that fire my alarms about these examples.
First, the unnecessary noisy syntax1 needed to pass the block that gets
evaluated when no object matched the
:detect condition2. I feel that
departs from the original intent of Ruby (to feel natural to the developer) by
forcing the use of ‘tech’ terms.
Second, the presence of repeated code. I don’t mind writing blocks if I really need to, but they seem overkill when all I want is to return an object to handle the “if none” case.
Dealing with duplicated code
In the Object Oriented paradigm repeated code is a symptom of a missing abstraction, a concept that is not being modeled. As a consequence, what would be its implementation is scattered around the methods that were supposed to use it.
It should be clear from the examples that repeated code does not mean repeated text, but repeated collaborations of message sends. These examples were choosen to demonstrate that repeating code involves repeating collaborations of message sends, instead of simply repeating text3.
Let’s take a look at what is repeated, and what is not, between the two uses of
1 2 3 4 5
Whenever we want to return an object when no object matches a condition we’re
wrapping it with a
lambda, because that is what the
:detect message expects
as first collaborator.
One way to go would be to add a new method to the
Enumerable module that does
the dirty lambda wrapping for us, but that solution would only work for
It would be great to come up with a more generic solution, one that works for any method that expects a block to be passed in.
Designing a generic solution
We would like is to be able to express these examples in a more natural way,
getting rid of the extra
1 2 3
Let’s start solving this “challenge” not only by writing a test first, but writing the test’s assert first:
1 2 3 4 5 6 7 8
After running it (and watching it turn red) the failure message reveales the root of the problem:
NoMethodError: undefined method `call' for #<Object:0x00000001390d68>
Besides the missing message implementation, what this really means is that
Object are not polymorphic with lambdas (instances of
with respect to the
To make this tests pass, let’s do the simplest thing possible and add the
:call method returning
But hey, we can do better! What about being able to pass an arbitraty object to
:select method (which expects an implicit block that takes one external
1 2 3 4 5 6 7 8 9 10 11
FruitFilter4 is a test class I used to express better the intent of the test.
This time, the failure message is a little more cryptic:
TypeError: wrong argument type Object (expected Proc)
We need an instance of
Proc to be passed in to
:select. The unary
operator converts blocks to procs, but
Object is not a
proc and neither
knows how to respond to the
:to_proc message that gets sent when using
Let’s do the simplest thing to pass the test, and implement
Object, returning a
proc that evaluates
self.call (that was implemented in
the first test).
Object looks like after passing the tests:
1 2 3 4 5 6 7 8 9 10 11
There are still some test cases left to be consider to make sure everything
works as expected/nothing was broken (like backwards compatibility with
What’s the deal with the “Value of Value”?
The need to reduce the friction when working with blocks was motivated by the
way Smalltalk solves the problem. Any object knows how to respond to the
#value message (which behaves in the same as the
:call message just
Even though the implementation is pretty straight forward, just returning
self, relying on
#value in Smalltalk allows us to express domain concepts
better, making the code easier to read by dealing with objects or blocks in the
From my point of view, it is a little method that adds great value.
By working the solution step by step through TDD, we’re now in a position to
explain and tell the cause of repeated code in the initial examples: instances
Object were not polymorphic with instances of
Proc. That forced us to
wrap objects in lambdas so
:detect could work as expected.
Two missing abstractions (methods, in this case) were implemented in
get rid of repeated code,
Besides the immediate benefit of the implemented feature, I really enjoyed the oportunity to strictly follow the OO paradigm and TDD to see how far they can be taken. I valued the fact that Ruby can be modified to make it fit my needs.
Do not take for granted a language is expressive or feels natural just because that’s what the documentations says and the community accepts it without questioning. Learn from the past (as we did from Smalltalk in this case) to avoid reinventing the flat tire.
Don’t get trapped in the language cliché. Build your own Ruby.
I’m talking about the
prockeywords preceding the braces needed to create a