Rules Are Meant to Be Broken: Ruby’s Shorthand for Methods that Don’t Take an Argument
All during elementary school, using ‘and’ at the beginning of a sentence got you a bad mark, yet by Junior year in High School if you did it artfully, the teacher couldn’t care less. You understood by then that not every sentence can start that way, but at times it’s appropriate for the flow of ideas.
Building a ‘Mental Model’
In the process of acquiring a new skill, we assimilate a number of rules outlining boundaries in which to work. It’s all new to us, so we have to trust that these rules are set out for a reason. George Leonard’s book ‘Mastery’ calls this “surrender”. We surrender to the teacher or process guiding us. Either the rules plainly cannot be broken (try you might), or there’s simply little benefit in doing so.
The benefit of learning about conjunctions like ‘and’ is that we see how in a normal sentence they can be used to bind related but not completely dependent ideas. As we practice conforming our writing to this principal, we learn to address 99% of use cases.
When learning a programming language like Ruby, we also learn to represent ideas and bind them with others. Some rules of the syntax allow us to perform operations within constraints. Learning the rules of the language takes time.
There is a shorthand in Ruby for performing a single operation on every member of a collection. It involves passing a method as a symbol to another iterative method invoked on a collection. The method referenced as a symbol takes advantage of a mechanism in Ruby wherein an argument prepended by
&, is passed as a block. The following example returns a new array with the corresponding integers of the collection converted to strings:
This shorthand syntax has a fundamental constraint. Or does it…
Strictly speaking, in order to invoke a method with Ruby’s shorthand, the method cannot take arguments. That being the case, if you write a custom method that takes an argument, you may assume that the shorthand syntax wouldn’t apply. I found myself in just that situation.
The challenge of an exercise at Launch School is to perform a base 8 integer conversion on a collection of integer objects. In order to do so, a base 10 to base 8 conversion method must itself be passed in shorthand.
Aware of the limitations, I got stuck. The skeleton code would require invoking a custom method that takes an argument with shorthand. I contrived a solution and ran the program hoping the conversion were performed. Argument error! Oh my! How was I to overcome this?
Into the Void
After looking at some documentation I realized that I needed to covert my method to a symbol in a very specific place. When Ruby encounters
& prepended to an argument, it treats that argument like a block. Since a block in Ruby is associated with procs, allowance is made for some interesting conversion. If the object that it encounters is a symbol, the
Symbol#toproc method is called.
A custom method that takes an argument can be converted to a proc then passed as a symbol to
&. The method to symbol conversion utilizes
Object#method which returns a
Method object that acts as a closure. The previously defined custom method is passed to
: like so:
to_proc to that method call returns the desired proc.
The star of the show here is
Object#method. Wait! That’s an instance method. How are we invoking it without an explicit receiver? An
Object class instance method invoked without an explicit receiver (line 5) has the implicit receiver
main, itself an instance of
main may access
Object#method. The documentation for
Method objects (http://ruby-doc.org/core-2.5.1/Method.html) states, “They may be used to invoke the method within the object, and as a block associated with an iterator.” So, if a
Method object can be treated like a block when passed to an iterator, passing a proc conversion of it to an iterative method is logical.
I made the adjustments, and the program ran seamlessly. Then, a brief epiphany!
I’d essentially written a custom method which takes an argument and through otherwise mundane conversions passed the method as a proc. The proc is passed without any explicit arguments. The rules had been broken.
Just as 99% of sentences don’t start with coordinating conjunctions, in most cases we don’t think of using this Ruby shorthand for custom methods. Here, for a method transforming a collection, this was a useful process to know. Further understanding it can surely yield more uses.
Take away: The Art of the Sword
In the movie ‘Hero’ with Jet Li (Spoiler Alert!), a master swordsman is bent on assassinating the king, so he allies himself with other assassins. All swordsmen in the film are at different levels of mastery.
The most advanced swordsman is the aptly named ‘Broken Sword’ who eventually abandons not only the sword but the plot of assassination. The King in threat studies a piece of calligraphy penned by Broken Sword and comes to a realization about mastering the sword. At each level, the swordsman is more and more dangerous. At one point, a blade of grass can be used as a sword; then, no sword at all. Curiously, at the last level of mastery the desire to fight is abandoned all together.
It’s not that the rules are really meant to limit us. In some ways, they protect us from disaster. In others, they shield us from attempting to address issues that are currently beyond us. Understanding our programming environment more and more, we perceive when given approaches are most appropriate. Some mental models must be re-evaluated, others completely abandoned.
Properly understanding and operating within the confounds of the rules guides us to understanding. Sufficient experience permits testing the boundaries. Understand your tools. Understand 99% of use cases. Otherwise operating against the norm won’t be favorable. Once we ‘get it’, we know when to break those hard and fast rules.