raganwald / homoiconic

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

This URL has Read+Write access

homoiconic / 2008-11-04 / quirky_birds_and_meta_syntactic_programming.markdown
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 1 Quirky Birds and Meta-Syntactic Programming
2 ---
3
29a8cd95 » raganwald 2008-11-05 expanded the explanation of... 4 In [Combinatory Logic](http://en.wikipedia.org/wiki/Combinatory_logic), the Queer Birds are a family of combinators which both parenthesize and permute. One member of the family, the *Quirky Bird*, has interesting implications for Ruby.
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 5
94aa33b7 » raganwald 2009-02-02 Updated links, especially t... 6 > As explained in [Kestrels](http://github.com/raganwald/homoiconic/tree/master/2008-10-29/kestrel.markdown#readme), the practice of nicknaming combinators after birds was 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.
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 7
46d61ac5 » Reg Braithwaite 2008-11-04 links and a picture 8
23cc9127 » raganwald 2008-11-07 smaller pic 9 [![Crazy Birds (c) 2008 Ross Lauderdale, some rights reserved](http://farm4.static.flickr.com/3164/2394551333_f0d5ce2e35.jpg)](http://www.flickr.com/photos/rosslauderdale/2394551333/ "Crazy Birds (c) 2008 Ross Lauderdale, some rights reserved")
46d61ac5 » Reg Braithwaite 2008-11-04 links and a picture 10
11
76173e7d » raganwald 2008-11-04 formatting fix 12 The quirky bird is written `Q`<sub>`3`</sub>`xyz = z(xy)`. In Ruby:
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 13
14 quirky.call(value_proc).call(a_value).call(a_proc)
15 => a_proc.call(value_proc.call(a_value))
16
c2533d34 » Reg Braithwaite 2008-11-04 a few more links, ready to ... 17 Like the [cardinal](http://github.com/raganwald/homoiconic/tree/master/2008-10-31/cardinal.rb "Songs of the Cardinal"), the quirky bird reverses the order of application. But where the cardinal modifies the function that is applied to a value, the quirky bird modifies the value itself. Let's compare how cardinals and quirky birds work.
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 18
19 **a cardinals refresher**
20
21 The cardinal is defined in its simplest Ruby form as:
22
23 cardinal.call(proc_over_proc).call(a_value).call(a_proc)
24 => proc_over_proc.call(a_proc).call(a_value)
25
26 From that definition, we wrote a method called `cardinal_define` that writes methods in idiomatic Ruby. For example, here's how we used `cardinal_define` to generate the `maybe` method:
27
28 cardinal_define(:maybe) do |a_proc|
29 lambda { |a_value|
30 a_proc.call(a_value) unless a_value.nil?
31 }
32 end
33
34 maybe(1) { |x| x + 1 }
35 => 2
36 maybe(nil) { |x| x + 1 }
37 => nil
38
39 Now we are not looking at the source code for `maybe`, but from the definition of a cardinal above we know that any method defined by `cardinal_define` will look roughly like:
40
41 def defined_by_a_cardinal(a_value, &a_proc)
42 proc_over_proc.call(a_proc).call(a_value)
43 end
44
45 Or in our case:
46
47 def maybe(a_value, &a_proc)
48 lambda do |a_proc|
49 lambda { |a_value|
50 a_proc.call(a_value) unless a_value.nil?
51 }
52 end.call(a_proc).call(a_value)
53 end
54
55 **and now to the quirky bird**
56
57 From the definition for the quirky bird, we expect that if we write `quirky_bird_define`, the methods it generates will look roughly like:
58
59 def defined_by_a_quirky_bird(a_value, &a_proc)
60 a_proc.call(value_proc.call(a_value))
61 end
62
63 So, are we ready to write `quirky_bird_define`? This seems too easy. Just copy the `cardinal_define` code, make a few changes, and we're done:
64
65 def quirky_bird_define(name, &value_proc)
66 define_method_taking_block(name) do |a_value, a_proc|
67 a_proc.call(value_proc.call(a_value))
68 end
69 end
70
71 # method_body_proc should expect (a_value, a_proc)
72 # see http://coderrr.wordpress.com/2008/10/29/using-define_method-with-blocks-in-ruby-18/
73 def define_method_taking_block(name, &method_body_proc)
74 self.class.send :define_method, "__quirky_bird_helper_#{name}__", method_body_proc
75 eval <<-EOM
76 def #{name}(a_value, &a_proc)
77 __quirky_bird_helper_#{name}__(a_value, a_proc)
78 end
79 EOM
80 end
81
82 Ok, let's try it out on something really trivial:
83
84 quirky_bird_define(:square_first) do |a_value|
85 a value * a_value
86 end
87
88 square_first(1) { |n| n + 1 }
89 => 2
90
91 square_first(2) { |n| n + 1 }
92 => 5
93
94 It works, good. Now let's define `maybe` using the quirky bird we just wrote. Just so we're clear, I want to write:
95
96 quirky_bird_define(:maybe) do |a_value|
97 # ... something goes here ...
98 end
99
100 maybe(1) { |n| n + 1 }
101 => 2
102
103 maybe(nil) { |n| n + 1 }
104 => nil
105
106 Scheisse! Figuring out what to put in the block to make `maybe` work is indeed queer and quirky!!
107
108 Now, the simple truth is, I know of no way to use a quirky bird to cover all of the possible blocks you could use with `maybe` so that it works exactly like the version of `maybe` we built with a cardinal. However, I have found that sometimes it is interesting to push an incomplete idea along if it is incomplete in interesting ways. "Maybe" we can learn something in the process.
109
110 **a limited interpretation of the quirky bird in Ruby**
111
112 Let's solve `maybe` any-which-way-we-can and see how it goes. When we used a cardinal, we wanted a proc that would modify another proc to such that if it was passed `nil`, it would answer `nil` without evaluating its contents.
113
46d61ac5 » Reg Braithwaite 2008-11-04 links and a picture 114 Now we want to modify a value such that if it is `nil`, it responds `nil` to the method `+`. This is doable, with the help of the `BlankSlate` class, also called a `BasicObject`. You'll find `BlankSlate` and `BasicObject` classes in various frameworks and Ruby 1.9, and there's one at [blank\_slate.rb](http://github.com/raganwald/homoiconic/tree/master/2008-11-04/blank_slate.rb "2008-11-04/blank_slate.rb at master from raganwald's homoiconic &mdash; GitHub") you can use.
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 115
116 `BlankSlate` is a class with no methods, which is very different from the base class `Object`. That's because `Object` in Ruby is *heavyweight*, it has lots of useful stuff. But we don't want useful stuff, because our mission is to answer a value that responds `nil` to any method you send it.
117
118 The Ruby way to handle any method is with `method_missing`. Here's a really simple expression that answers an object that responds `nil` to any method:
119
120 returning(BlankSlate.new) do |it|
121 def it.method_missing(*args)
122 nil
123 end
124 end
125
126 Hmmm. What about:
127
128 quirky_bird_define(:maybe) do |value|
129 if value.nil?
130 returning(BlankSlate.new) do |it|
131 def it.method_missing(*args)
132 nil
133 end
134 end
135 else
136 value
137 end
138 end
139
29a8cd95 » raganwald 2008-11-05 expanded the explanation of... 140 This is saying, "Let's define a quirky bird method based on a `value_proc` as usual. Our `value_proc` will take a value, and if the value is `nil` we will return an object that responds with `nil` to any method. But if the value is not nil, our `value_proc` will respond with the object."
141
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 142 Let's try it:
143
144 maybe(1) { |n| n + 1 }
145 => 2
146
147 maybe(nil) { |n| n + 1 }
148 => nil
149
29a8cd95 » raganwald 2008-11-05 expanded the explanation of... 150 Now, I admit this is *very* flawed:
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 151
152 maybe(nil) { |n| n + 1 + 1 }
153 => NoMethodError: undefined method `+' for nil:NilClass
29a8cd95 » raganwald 2008-11-05 expanded the explanation of... 154
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 155 maybe(nil) { |n| 1 + n }
156 => TypeError: coerce must return [x, y]
157
158 The basic problem here is that we only control the value we pass in. We cant modify how other objects respond to it, nor can we control what happens to any objects we return from methods called on it. So, the quirky bird turns out to be useful in the case where (a) the value is the receiver of a method, and (b) there is only one method being called, not a chain of methods.
159
160 Hmmm again.
161
162 **embracing the quirky bird's nature**
163
29a8cd95 » raganwald 2008-11-05 expanded the explanation of... 164 Maybe we shouldn't be generating methods that deal with arbitrary blocks and procedures. One way to scale this down is to deal only with single method invocations. For example, what if instead of designing our new version of `maybe` so that we invoke it by writing `maybe(nil) { |n| n + 1 }` or `maybe(1) { |n| n + 1 }`, we design it so that we write `nil.maybe + 1` or `1.maybe + 1` instead?
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 165
29a8cd95 » raganwald 2008-11-05 expanded the explanation of... 166 In that case, `maybe` becomes a method on the object class that applies `value_proc` to its receiver rather than being a method that takes a value and a block. Getting down to business, we are going to open the core `Object` class and add a new method to it. The body of that method will be our `value_proc`:
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 167
168 def quirky_bird_extend(name, &value_proc)
169 Object.send(:define_method, name) do
170 value_proc.call(self)
171 end
172 end
173
29a8cd95 » raganwald 2008-11-05 expanded the explanation of... 174 Just as we said, we are defining a new method in the `Object` class.
175
176 > We are using `define_method` and a block rather than the `def` keyword. The reason is that when we use `define_method` and a block, the body of the method executes in the context of the block, not the context of the object itself. Blocks are closures in Ruby, which means that the block has access to `value_proc`, the parameter from our `quirky_bird_extend` method.
177
178 > Had we used `def`, Ruby would try to evaluate `value_proc` in the context of the object itself. So our parameter would be lost forever. Performance wonks and compiler junkies will be interested in this behaviour, as it has very serious implications for garbage collection and memory leaks.
179
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 180 Now let's use it with exactly the same block we used with `quirky_bird_define`:
181
182 require 'quirky_bird'
183 require 'blank_slate'
184 require 'returning'
185
186 quirky_bird_extend(:maybe) do |value|
187 if value.nil?
188 returning(BlankSlate.new) do |it|
189 def it.method_missing(*args)
190 nil
191 end
192 end
193 else
194 value
195 end
196 end
197
198 nil.maybe + 1
199 => nil
29a8cd95 » raganwald 2008-11-05 expanded the explanation of... 200
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 201 1.maybe + 1
202 => 2
203
29a8cd95 » raganwald 2008-11-05 expanded the explanation of... 204 It works. And it looks familiar! We have defined our own version of [andand](http://github.com/raganwald/andand/tree "sudo gem install andand"), only this is much more **interesting**. Instead of a one-off handy-dandy, we have created a method that creates similar methods.
205
21e665e9 » raganwald 2008-11-05 credit where credit is due 206 Let's try it again, this time emulating Chris Wanstrath's [try](http://ozmm.org/posts/try.html):
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 207
208 quirky_bird_extend(:try) do |value|
209 returning(BlankSlate.new) do |it|
210 def it.__value__=(arg)
211 @value = arg
212 end
213 def it.method_missing(name, *args)
214 if @value.respond_to?(name)
215 @value.send(name, *args)
216 end
217 end
218 it.__value__ = value
219 end
220 end
221
222 nil.try + 1
223 => nil
29a8cd95 » raganwald 2008-11-05 expanded the explanation of... 224
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 225 1.try + 1
226 => 2
29a8cd95 » raganwald 2008-11-05 expanded the explanation of... 227
d5689944 » Reg Braithwaite 2008-11-04 Quirky Birds Skeleton 228 1.try.ordinalize
229 => nil
230
231 As you can see, we can used the quirky bird to create a whole family of methods that modify the receiver in some way to produce new semantics. I can't show you the source code, but here is something from a proprietary Rails application:
232
233 Account.without_requiring_authorization.create!(...)
234
235 In this case, `without_requiring_authorization` follows the quirky bird pattern, only instead of taking an instance and producing a version that handles certain methods specially, this one takes a class and produces a version that doesn't enforce authorization for use in test cases.
236
237 **so what have we learned?**
238
46d61ac5 » Reg Braithwaite 2008-11-04 links and a picture 239 The quirky bird is superficially similar to the cardinal, however it can be used to generate syntax that is a little more method-oriented rather than function-oriented. And what's better than a handy method like andand? A method for defining such methods, of course.
240
241 * [blank_slate.rb](http://github.com/raganwald/homoiconic/tree/master/2008-11-04/blank_slate.rb "")
242 * [returning.rb](http://github.com/raganwald/homoiconic/tree/master/2008-11-04/returning.rb "")
243 * [quirky_bird.rb](http://github.com/raganwald/homoiconic/tree/master/2008-11-04/quirky_bird.rb "")
244 * [quirky_songs.rb](http://github.com/raganwald/homoiconic/tree/master/2008-11-04/quirky_songs.rb "")
6a290266 » Reg Braithwaite 2008-11-04 a challenge for readers 245
0f97bf06 » raganwald 2009-06-29 Wrapping Combinators 246 _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). By the way, did anybody spot the [Kestrel]((http://github.com/raganwald/homoiconic/tree/master/2008-10-29/kestrel.markdown#readme) in today's post?
ae7e6254 » Reg Braithwaite 2008-11-04 speaking of endings, we nee... 247
65c6f213 » raganwald 2008-11-05 248 p.s. Please vote for this post on [ruby.reddit.com](http://www.reddit.com/r/ruby/comments/7bhsh/quirky_birds_and_metasyntactic_programming/) and [hacker news](http://news.ycombinator.com/item?id=354660)!
5713d222 » raganwald 2008-11-05 added shameless upmod whoring 249
ae7e6254 » Reg Braithwaite 2008-11-04 speaking of endings, we nee... 250 ---
27017272 » Reg Braithwaite 2008-11-05 new footer 251
f0aad1ee » Reg Braithwaite 2008-12-02 and simplified the footers ... 252 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 253
a4bfc714 » raganwald 2009-09-23 updated resume link 254 Reg Braithwaite: [Home Page](http://reginald.braythwayt.com), [CV](http://reginald.braythwayt.com/RegBraithwaiteGH0909_en_US.pdf ""), [Twitter](http://twitter.com/raganwald)