Skip to content


Subversion checkout URL

You can clone with
Download ZIP
100644 160 lines (107 sloc) 9.41 KB
b6daf43 Added a post about Kestrels
Reg Braithwaite authored
1 Kestrels
2 ---
8e36a59 @raganwald Added the actual combinatory logic notation for the K Combinator.
raganwald authored
4 In [Combinatory Logic](, a Kestrel is a function that returns a constant function, normally written `Kxy = x`. In Ruby, it might look like this:
b6daf43 Added a post about Kestrels
Reg Braithwaite authored
6 # for *any* x,
8 => :foo
3182f68 @raganwald
raganwald authored
10 Although its formal name is the "K Combinator," it is more popularly named a Kestrel following the lead 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.
e97e5f8 @raganwald
raganwald authored
473057a @raganwald
raganwald authored
13 [![Kestrel (c) 2006 Ian Turk, some rights reserved](]( "Kestrel (c) 2006 Ian Turk, some rights reserved")
3182f68 @raganwald
raganwald authored
6b7c006 @raganwald Added a photo of a kestrel.
raganwald authored
ed54090 FInished up a note about Kestrels in Ruby
Reg Braithwaite authored
16 Kestrels are to be found in Ruby. You may be familiar with their Ruby 1.9 name, `#tap`. Let's say you have a line like `address = Person.find(...).address` and you wish to log the person instance. With `tap`, you can inject some logging into the expression without messy temporary variables:
b6daf43 Added a post about Kestrels
Reg Braithwaite authored
18 address = Person.find(...).tap { |p| logger.log "person #{p} found" }.address
20 `tap` is a method in all objects that passes `self` to a block and returns self, ignoring whatever the last item of the block happens to be. Ruby on Rails programmers will recognize the Kestrel in slightly different form:
22 address = returning Person.find(...) do |p|
23 logger.log "person #{p} found"
24 end.address
26 Again, the result of the block is discarded, it is only there for side effects. This behaviour is the same as a Kestrel. Remember ``? If I rewrite it like this, you can see the similarity:
28 do
29 x
30 end
31 => :foo
33 Both `returning` and `tap` are handy for grouping side effects together. Methods that look like this:
35 def registered_person(params = {})
36 person = => true))
37 Registry.register(person)
38 person.send_email_notification
39 person
40 end
42 Can be rewritten using `returning`:
44 def registered_person(params = {})
45 returning => true)) do |person|
46 Registry.register(person)
47 person.send_email_notification
48 end
49 end
51 It is obvious from the first line what will be returned and it eliminates an annoying error when the programmer neglects to make `person` the last line of the method.
ed54090 FInished up a note about Kestrels in Ruby
Reg Braithwaite authored
53 **object initializer blocks**
a2ba991 @raganwald Fix some markdown that github doesn't like
raganwald authored
55 The Kestrel has also been sighted in the form of *object initializer blocks*. Consider this example using [Struct]( "All about Struct"):
b6daf43 Added a post about Kestrels
Reg Braithwaite authored
57 Contact =, :last, :email) do
58 def to_hash
59 Hash[*]
60 end
61 end
63 The method `Struct#new` creates a new class. It also accepts an optional block, evaluating the block for side effects only. It returns the new class regardless of what happens to be in the block (it happens to evaluate the block in class scope, a small refinement).
65 You can use this technique when writing your own classes:
67 class Bird < Creature
68 def initialize(*params)
69 # do something with the params
70 yield self if block_given?
71 end
72 end
74 Forest.add(
75 => 'Kestrel) { |k| combinators << k }
76 )
78 The pattern of wanting a Kestrel/returning/tap when you create a new object is so common that building it into object initialization is useful. And in fact, it's built into `ActiveRecord`. Methods like `new` and `create` take optional blocks, so you can write:
80 class Person < ActiveRecord::Base
81 # ...
82 end
84 def registered_person(params = {})
633378d @raganwald Fixed a missing parenthesis
raganwald authored
85 => true)) do |person|
b6daf43 Added a post about Kestrels
Reg Braithwaite authored
86 Registry.register(person)
87 person.send_email_notification
88 end
89 end
1fcbfb7 @ab9 Fixed grammar.
ab9 authored
91 In Rails, `returning` is not necessary when creating instances of your model classes, thanks to ActiveRecord's built-in object initializer blocks.
ed54090 FInished up a note about Kestrels in Ruby
Reg Braithwaite authored
93 **a variation on the kestrel**
95 When we discussed `Struct` above, we noted that its initializer block has a slightly different behaviour than `tap` or `returning`. It takes an initializer block, but it doesn't pass the new class to the block as a parameter, it evaluates the block in the context of the new class.
97 Putting this into implementation terms, it evaluates the block with `self` set to the new class. This is not the same as `returning` or `tap`, both of which leave `self` untouched. We can write our own version of `returning` with the same semantics. We will call it `inside`:
99 module Kernel
101 def inside(value, &block)
102 value.instance_eval(&block)
103 value
104 end
106 end
108 You can use this variation on a Kestrel just like `returning`, only you do not need to specify a parameter:
110 inside [1, 2, 3] do
111 uniq!
112 end
113 => [1, 2, 3]
115 This isn't particularly noteworthy. Of more interest is your access to private methods and instance variables:
117 sna ='Fubar') do
118 attr_reader :fu
121 inside(sna) do
122 @fu = 'bar'
123 end
124 => <struct Struct::Fubar >
126 sna.fu
127 => 'bar'
129 `inside` is a Kestrel just like `returning`. No matter what value its block generates, it returns its primary argument. The only difference between the two is the evaluation environment of the block.
b6daf43 Added a post about Kestrels
Reg Braithwaite authored
131 So what have we learned?
ed54090 FInished up a note about Kestrels in Ruby
Reg Braithwaite authored
133 1. `tap`, `returning`, and `inside` are useful;
b6daf43 Added a post about Kestrels
Reg Braithwaite authored
134 2. "Impractical" Computer Science isn't, and;
ed54090 FInished up a note about Kestrels in Ruby
Reg Braithwaite authored
135 3. [To Mock a Mockingbird]( belongs on your bookshelf if it isn't there already. The Kestrel is just one bird. Imagine what code you could write with a forest of them at your fingertips!
137 **post scriptum**
139 * `returning` is part of Ruby on Rails. `tap` is part of Ruby 1.9. It is available for Ruby 1.8 as part of the [andand gem]( `sudo gem install andand`.
098de93 @raganwald fixed links
raganwald authored
140 * [inside.rb]( If you are using Rails, drop it in `config/initializers` to make it available in your project.
94aa33b @raganwald Updated links, especially to combinators
raganwald authored
141 * [You keep using that idiom]( I do not think it means what you think it means.
58ddf1e @raganwald Merge branch 'master' of
raganwald authored
fe91187 @raganwald mockingbird links and removing the "NEW!"
raganwald authored
143 _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](
b6daf43 Added a post about Kestrels
Reg Braithwaite authored
7d4a695 @raganwald dasherize the mores
raganwald authored
145 ---
f69ce70 @raganwald Link to the book
raganwald authored
8c4fb37 @raganwald My recent work
raganwald authored
147 My recent work:
f9d4487 @raganwald allongé
raganwald authored
149 ![]([![JavaScript Allongé](]( "JavaScript Allongé")![]([![CoffeeScript Ristretto](]( "CoffeeScript Ristretto")![]([![Kestrels, Quirky Birds, and Hopeless Egocentricity](]( "Kestrels, Quirky Birds, and Hopeless Egocentricity")
4479986 @raganwald More recent work!
raganwald authored
f9d4487 @raganwald allongé
raganwald authored
151 * "[JavaScript Allongé](,[CoffeeScript Ristretto](", "[Kestrels, Quirky Birds, and Hopeless Egocentricity](" and my [other books](
21fcc00 @raganwald redirect to
raganwald authored
152 * [](, practical function combinators and decorators for JavaScript.
17be325 @raganwald revised footers
raganwald authored
153 * [Method Combinators](, a CoffeeScript/JavaScript library for writing method decorators, simply and easily.
4f7cce6 @raganwald githiub
raganwald authored
154 * [jQuery Combinators](, what else? A jQuery plugin for writing your own fluent, jQuery-like code.
f69ce70 @raganwald Link to the book
raganwald authored
16fa4bb @raganwald separated .sig
raganwald authored
156 ---
b2b5c4a @raganwald shuffle
raganwald authored
158 (Spot a bug or a spelling mistake? This is a Github repo, fork it and send me a pull request!)
7e71700 @raganwald updated links
raganwald authored
160 [Reg Braithwaite]( | [@raganwald](
Something went wrong with that request. Please try again.