raganwald / homoiconic

An experiment in publishing code and words about code on a small scale.

This URL has Read+Write access

homoiconic / 2008-10-29 / kestrel.markdown
b6daf436 » Reg Braithwaite 2008-10-28 Added a post about Kestrels 1 Kestrels
2 ---
3
8e36a596 » raganwald 2008-10-29 Added the actual combinator... 4 In [Combinatory Logic](http://en.wikipedia.org/wiki/Combinatory_logic), a Kestrel is a function that returns a constant function, normally written `Kxy = x`. In Ruby, it might look like this:
b6daf436 » Reg Braithwaite 2008-10-28 Added a post about Kestrels 5
6 # for *any* x,
7 kestrel.call(:foo).call(x)
8 => :foo
9
3182f684 » raganwald 2008-10-29 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](http://www.amazon.com/gp/product/0192801422?ie=UTF8&tag=raganwald001-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=0192801422). 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.
11
e97e5f89 » raganwald 2008-10-29 12
473057aa » raganwald 2008-10-29 13 [![Kestrel (c) 2006 Ian Turk, some rights reserved](http://farm1.static.flickr.com/99/298991569_911a900738.jpg)](http://www.flickr.com/photos/ianturk/298991569/ "Kestrel (c) 2006 Ian Turk, some rights reserved")
3182f684 » raganwald 2008-10-29 14
6b7c006a » raganwald 2008-10-29 Added a photo of a kestrel. 15
ed540902 » Reg Braithwaite 2008-10-29 FInished up a note about Ke... 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:
b6daf436 » Reg Braithwaite 2008-10-28 Added a post about Kestrels 17
18 address = Person.find(...).tap { |p| logger.log "person #{p} found" }.address
19
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:
21
22 address = returning Person.find(...) do |p|
23 logger.log "person #{p} found"
24 end.address
25
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 `kestrel.call(:foo).call(x)`? If I rewrite it like this, you can see the similarity:
27
28 Kestrel.call(:foo) do
29 x
30 end
31 => :foo
32
33 Both `returning` and `tap` are handy for grouping side effects together. Methods that look like this:
34
35 def registered_person(params = {})
36 person = Person.new(params.merge(:registered => true))
37 Registry.register(person)
38 person.send_email_notification
39 person
40 end
41
42 Can be rewritten using `returning`:
43
44 def registered_person(params = {})
45 returning Person.new(params.merge(:registered => true)) do |person|
46 Registry.register(person)
47 person.send_email_notification
48 end
49 end
50
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.
52
ed540902 » Reg Braithwaite 2008-10-29 FInished up a note about Ke... 53 **object initializer blocks**
54
a2ba991e » raganwald 2009-05-24 Fix some markdown that gith... 55 The Kestrel has also been sighted in the form of *object initializer blocks*. Consider this example using [Struct](http://blog.grayproductions.net/articles/all_about_struct "All about Struct"):
b6daf436 » Reg Braithwaite 2008-10-28 Added a post about Kestrels 56
57 Contact = Struct.new(:first, :last, :email) do
58 def to_hash
59 Hash[*members.zip(values).flatten]
60 end
61 end
62
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).
64
65 You can use this technique when writing your own classes:
66
67 class Bird < Creature
68 def initialize(*params)
69 # do something with the params
70 yield self if block_given?
71 end
72 end
73
74 Forest.add(
75 Bird.new(:name => 'Kestrel) { |k| combinators << k }
76 )
77
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:
79
80 class Person < ActiveRecord::Base
81 # ...
82 end
83
84 def registered_person(params = {})
633378d7 » raganwald 2008-10-29 Fixed a missing parenthesis 85 Person.new(params.merge(:registered => true)) do |person|
b6daf436 » Reg Braithwaite 2008-10-28 Added a post about Kestrels 86 Registry.register(person)
87 person.send_email_notification
88 end
89 end
90
ed540902 » Reg Braithwaite 2008-10-29 FInished up a note about Ke... 91 In Rails, `returning` is not necessary when creating instances your model classes, thanks to ActiveRecord's built-in object initializer blocks.
92
93 **a variation on the kestrel**
94
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.
96
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`:
98
99 module Kernel
100
101 def inside(value, &block)
102 value.instance_eval(&block)
103 value
104 end
105
106 end
107
108 You can use this variation on a Kestrel just like `returning`, only you do not need to specify a parameter:
109
110 inside [1, 2, 3] do
111 uniq!
112 end
113 => [1, 2, 3]
114
115 This isn't particularly noteworthy. Of more interest is your access to private methods and instance variables:
116
117 sna = Struct.new('Fubar') do
118 attr_reader :fu
119 end.new
120
121 inside(sna) do
122 @fu = 'bar'
123 end
124 => <struct Struct::Fubar >
125
126 sna.fu
127 => 'bar'
128
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.
b6daf436 » Reg Braithwaite 2008-10-28 Added a post about Kestrels 130
131 So what have we learned?
132
ed540902 » Reg Braithwaite 2008-10-29 FInished up a note about Ke... 133 1. `tap`, `returning`, and `inside` are useful;
b6daf436 » Reg Braithwaite 2008-10-28 Added a post about Kestrels 134 2. "Impractical" Computer Science isn't, and;
ed540902 » Reg Braithwaite 2008-10-29 FInished up a note about Ke... 135 3. [To Mock a Mockingbird](http://www.amazon.com/gp/product/0192801422?ie=UTF8&tag=raganwald001-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=0192801422) 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!
136
137 **post scriptum**
138
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](http://andand.rubyforge.org). `sudo gem install andand`.
098de933 » raganwald 2008-10-29 fixed links 140 * [inside.rb](http://github.com/raganwald/homoiconic/tree/master/2008-10-29/inside.rb): If you are using Rails, drop it in `config/initializers` to make it available in your project.
94aa33b7 » raganwald 2009-02-02 Updated links, especially t... 141 * [You keep using that idiom](http://github.com/raganwald/homoiconic/tree/master/2008-10-29/you_keep_using_that_idiom.markdown#readme). I do not think it means what you think it means.
58ddf1e1 » raganwald 2009-06-29 Merge branch 'master' of gi... 142
143 _More on combinators_: [Kestrels](http://github.com/raganwald/homoiconic/tree/master/2008-10-29/kestrel.markdown#readme), [The Thrush](http://github.com/raganwald/homoiconic/tree/master/2008-10-30/thrush.markdown#readme), [Songs of the Cardinal](http://github.com/raganwald/homoiconic/tree/master/2008-10-31/songs_of_the_cardinal.markdown#readme), [Quirky Birds and Meta-Syntactic Programming](http://github.com/raganwald/homoiconic/tree/master/2008-11-04/quirky_birds_and_meta_syntactic_programming.markdown#readme), [Aspect-Oriented Programming in Ruby using Combinator Birds](http://github.com/raganwald/homoiconic/tree/master/2008-11-07/from_birds_that_compose_to_method_advice.markdown#readme), [The Enchaining and Obdurate Kestrels](http://github.com/raganwald/homoiconic/tree/master/2008-11-12/the_obdurate_kestrel.md#readme), [Finding Joy in Combinators](http://github.com/raganwald/homoiconic/tree/master/2008-11-16/joy.md#readme), [Refactoring Methods with Recursive Combinators](http://github.com/raganwald/homoiconic/tree/master/2008-11-23/recursive_combinators.md#readme), [Practical Recursive Combinators](http://github.com/raganwald/homoiconic/tree/master/2008-11-26/practical_recursive_combinators.md#readme), [The Hopelessly Egocentric Blog Post](http://github.com/raganwald/homoiconic/tree/master/2009-02-02/hopeless_egocentricity.md#readme), and [Wrapping Combinators](http://github.com/raganwald/homoiconic/tree/master/2009-06-29/wrapping_combinators.md#readme).
b6daf436 » Reg Braithwaite 2008-10-28 Added a post about Kestrels 144
145 ---
27017272 » Reg Braithwaite 2008-11-05 new footer 146
f0aad1ee » Reg Braithwaite 2008-12-02 and simplified the footers ... 147 Subscribe to [new posts and daily links](http://feeds.feedburner.com/raganwald "raganwald's rss feed"): <a href="http://feeds.feedburner.com/raganwald"><img src="http://feeds.feedburner.com/~fc/raganwald?bg=&amp;fg=&amp;anim=" height="26" width="88" style="border:0" alt="" align="top"/></a>
27017272 » Reg Braithwaite 2008-11-05 new footer 148
a4bfc714 » raganwald 2009-09-23 updated resume link 149 Reg Braithwaite: [Home Page](http://reginald.braythwayt.com), [CV](http://reginald.braythwayt.com/RegBraithwaiteGH0909_en_US.pdf ""), [Twitter](http://twitter.com/raganwald)