In Combinatory Logic, the thrush is an extremely simple permuting combinator; it reverses the normal order of evaluation.
As explained in Kestrels, the practice of nicknaming combinators after birds was established in Raymond Smullyan's amazing book To Mock a Mockingbird. In this book, Smullyan explains combinatory logic and derives a number of important results by presenting the various combinators as songbirds in a forest. Since the publication of the book more than twenty years ago, the names he gave the birds have become standard nicknames for the various combinators.
The thrush is written Txy = yx
. It reverses evaluation. In Ruby terms,
thrush.call(a_value).call(a_proc)
=> a_proc.call(a_value)
In No Detail Too Small, I defined Object#into
, an implementation of the thrush as a Ruby method:
class Object
def into expr = nil
expr.nil? ? yield(self) : expr.to_proc.call(self)
end
end
If you are in the habit of violating the Law of Demeter, you can use #into
to make an expression read consistently from left to right. For example, this code:
lambda { |x| x * x }.call((1..100).select(&:odd?).inject(&:+))
Reads "Square (take the numbers from 1 to 100, select the odd ones, and take the sum of those)." Confusing. Whereas with #into
, you can write:
(1..100).select(&:odd?).inject(&:+).into { |x| x * x }
Which reads "Take the numbers from 1 to 100, keep the odd ones, take the sum of those, and then answer the square of that number."
A permuting combinator like #into
is not strictly necessary when you have parentheses or local variables. Which is kind of interesting, because it shows that if you have permuting combinators, you can model parentheses and local variables.
But we are not interested in theory. #into
may be equivalent to what we can accomplish with other means, but it is useful to us if we feel it makes the code clearer and easier to understand. Sometimes a longer expression should be broken into multiple small expressions to make it easier to understand. Sometimes it can be reordered using tools like #into
.
another thrush
Object#into
defines the thrush as a method that takes a block, lambda, or anything that can become a block or lambda as its argument. There is another way to formulate a Thrush:
class Kernel
def let it
yield it
end
end
It's remarkably simple, so simple that it appears to be less useful than #into
. The example above would look like this if we used let
:
let (1..100).select(&:odd?).inject(&:+) do |x|
x * x
end
How does that help? I'll let you in on a secret: Ruby 1.9 changes the game. In Ruby 1.8, x
is local to the surrounding method, so it doesn't help. But in Ruby 1.9, x
is a block local variable, meaning that it does not clobber an existing variable. So in Ruby 1.8:
def say_the_square_of_the_sum_of_the_odd_numbers(x)
sotos = let (1..x).select(&:odd?).inject(&:+) do |x|
x * x
end
"The square of the sum of the odd numbers from 1..#{x} is #{sotos}"
end
say_the_square_of_the_sum_of_the_odd_numbers(10)
=> "The square of the sum of the odd numbers from 1..25 is 625"
1..25
!? What happened here is that the x
inside the block clobbered the value of the x
parameter. Not good. In Ruby 1.9:
say_the_square_of_the_sum_of_the_odd_numbers(10)
=> "The square of the sum of the odd numbers from 1..10 is 625"
Much better, Ruby 1.9 creates a new scope inside the block and x
is local to that block, shadowing the x
parameter. Now we see a use for let
:
let(some_expression) do |my_block_local_variable|
# ...
end
let
creates a new scope and defines your block local variable inside the block. This signals that the block local variable is not used elsewhere. Imperative methods can be easier to understand when they are composed of smaller blocks with well-defined dependencies between them. A variable local to the entire method creates a dependency across the entire method. A variable local to a block only creates dependencies within that block.
Although Ruby 1.8 does not enforce this behaviour, it can be useful to write code in this style as a signal to make the code easier to read.
summary
We have seen two formulations of the thrush combinator, #into
and let
. One is useful for making expressions more consistent and easier to read, the other for signaling the scope of block-local variables.
If you are using Rails, drop these in config/initializers to make them available in your project. let
is also available as part of the ick gem, along with a more powerful variation, lets
. To get it, simply sudo gem install ick
.
More on combinators: Kestrels, The Thrush, Songs of the Cardinal, Quirky Birds and Meta-Syntactic Programming, Aspect-Oriented Programming in Ruby using Combinator Birds, The Enchaining and Obdurate Kestrels, Finding Joy in Combinators, Refactoring Methods with Recursive Combinators, Practical Recursive Combinators, The Hopelessly Egocentric Blog Post, Wrapping Combinators, and Mockingbirds and Simple Recursive Combinators in Ruby.
My recent work:
- JavaScript Allongé, CoffeeScript Ristretto, and my other books.
- allong.es, practical function combinators and decorators for JavaScript.
- Method Combinators, a CoffeeScript/JavaScript library for writing method decorators, simply and easily.
- jQuery Combinators, what else? A jQuery plugin for writing your own fluent, jQuery-like code.
(Spot a bug or a spelling mistake? This is a Github repo, fork it and send me a pull request!)