tag:blogger.com,1999:blog-65392052090920927422024-02-20T14:28:43.591+01:00Smalltalking about RubyRobert Doberhttp://www.blogger.com/profile/06304126315464640783noreply@blogger.comBlogger8125tag:blogger.com,1999:blog-6539205209092092742.post-58942061936873452442009-07-29T13:00:00.004+02:002009-08-03T15:02:21.668+02:00How to module_evalIn my whole Ruby Metaprogrammer life I have never used any <i>*_eval string</i>.<br />This was a deliberate choice I made and I still believe that it was reasonable in the sense that it suits my style. Personally I was able to structure and refactor my metacode much better, well, I thought...<br /><br />Now I am working on code that shall be used as a library, closely related to core, thus, hopefully widely used. I am not a fan of premature optimization. I often use methods like <code class="code1">Enumerable#inject</code> for there elegance not worrying at all about the speed.<br />This is the exception<br />But then there is this of course:Robert Doberhttp://www.blogger.com/profile/06304126315464640783noreply@blogger.com2tag:blogger.com,1999:blog-6539205209092092742.post-73903648997168176322009-07-27T15:20:00.017+02:002009-07-27T15:47:51.558+02:00Why I use VerifyI was just "specing" up my <span style="font-style: italic;">Thread Safe Wrappers</span> when I stumbled about methods on Array I had never seen before.Eg. <code style="font-size: 10pt">#taguri=</code>. Realizing that it ( and some others ) were introduced by <i>rspec</i> I had a choice between a rock and a hard place: Either not using introspection in my definitions of methods in the Array wrapper (and probably the same for Hash, String and friends), or deleting those methods manually.<br />The first choice is just impossible because it would mean lots and lots of typing, which is just unacceptable, and the second choice would mean that there would be code in the production code which is there for the sole purpose of not implementing methods from the Rspec framework.<br /><br />It occurred to me than that <a href="http://labrador.rubyforge.org/#verify">Verify</a> has been written for that exact reason by YHS. Not to step on the toes of the testee!<br /><br />I immediately started to rewrite my *_spec.rb files into *_verify.rb files (not that this convention is enforced by <i>Verify</i> but it seems good practice ;). And it was just then that I realized I did not know my API (as lean as it is) by heart and I had to go into the examples folder of my GIT repository to find it.<br /><br />That is unacceptable, we need this stuff on the web, right?<br /><br />Here we go then:<br /><div style="background-color: #666666; text: #ffffff;padding: 20px;margin-top: 15px;margin-left: 30px; font-size: 11pt;"><br /><font face="monospace"><font color="#ffff00"> 7 </font><font color="#cd5c5c">require</font> <font color="#ffdead">'</font><font color="#ffa0a0">mockify</font><font color="#ffdead">'</font><br /><font color="#ffff00"> 8 </font><font color="#cd5c5c">require</font> <font color="#ffdead">'</font><font color="#ffa0a0">verify</font><font color="#ffdead">'</font><br /><font color="#ffff00"> 9 </font><br /><font color="#ffff00">10 </font><font color="#bdb76b"><b>Verify</b></font> <font color="#ffdead">"</font><font color="#ffa0a0">Some Important Facts</font><font color="#ffdead">"</font> <font color="#f0e68c"><b>do</b></font><br /><font color="#ffff00">11 </font> verify <font color="#ffdead">"</font><font color="#ffa0a0">42 is true</font><font color="#ffdead">"</font> <font color="#f0e68c"><b>do</b></font><br /><font color="#ffff00">12 </font> <font color="#ffa0a0">42</font><br /><font color="#ffff00">13 </font> <font color="#f0e68c"><b>end</b></font><br /><font color="#ffff00">14 </font> refute <font color="#ffdead">"</font><font color="#ffa0a0">42 is pretty big</font><font color="#ffdead">"</font> <font color="#f0e68c"><b>do</b></font><br /><font color="#ffff00">15 </font> <font color="#ffa0a0">42</font> > <font color="#ffa0a0">100_000</font><br /><font color="#ffff00">16 </font> <font color="#f0e68c"><b>end</b></font><br /><font color="#ffff00">17 </font> verify_exceptions <font color="#bdb76b"><b>NoMethodError</b></font> <font color="#f0e68c"><b>do</b></font><br /><font color="#ffff00">18 </font> <font color="#ffa0a0">42</font>.unrelinguished!<br /><font color="#ffff00">19 </font> <font color="#f0e68c"><b>end</b></font><br /><font color="#ffff00">20 </font><font color="#f0e68c"><b>end</b></font><br /><font color="#ffff00">21 </font><br /><font color="#ffff00">22 </font><font color="#bdb76b"><b>Verify</b></font> <font color="#ffdead">"</font><font color="#ffa0a0">With parameters</font><font color="#ffdead">"</font> <font color="#f0e68c"><b>do</b></font><br /><font color="#ffff00">23 </font> verify msg: <font color="#ffdead">"</font><font color="#ffa0a0">Everything is well</font><font color="#ffdead">"</font>, target: <font color="#ffa0a0">42</font>, actual: <font color="#ffa0a0">21</font> * <font color="#ffa0a0">2</font><br /><font color="#ffff00">24 </font> refute msg: <font color="#ffdead">"</font><font color="#ffa0a0">But not too much</font><font color="#ffdead">"</font>, target: <font color="#ffa0a0">42</font>, actual: <font color="#ffa0a0">41</font><br /><font color="#ffff00">25 </font><font color="#f0e68c"><b>end</b></font><br /><font color="#ffff00">26 </font><br /><font color="#ffff00">27 </font><font color="#bdb76b"><b>Verify</b></font> <font color="#ffdead">"</font><font color="#ffa0a0">Capturing stdout</font><font color="#ffdead">"</font> <font color="#f0e68c"><b>do</b></font><br /><font color="#ffff00">28 </font> out = with_output <font color="#f0e68c"><b>do</b></font> | <font color="#98fb98">o</font> |<br /><font color="#ffff00">29 </font> puts <font color="#ffa0a0">42</font><br /><font color="#ffff00">30 </font> verify target: <font color="#ffdead">"</font><font color="#ffa0a0">42</font><font color="#ffdead">\n</font><font color="#ffdead">"</font>, actual: o.string<br /><font color="#ffff00">31 </font> puts<br /><font color="#ffff00">32 </font> verify target: <font color="#ffdead">"</font><font color="#ffa0a0">42</font><font color="#ffdead">\n\n</font><font color="#ffdead">"</font>, actual: o.string<br /><font color="#ffff00">33 </font> <font color="#f0e68c"><b>end</b></font><br /><font color="#ffff00">34 </font> verify msg: <font color="#ffdead">"</font><font color="#ffa0a0">with_output converted out to an array of lines</font><font color="#ffdead">"</font>,<br /><font color="#ffff00">35 </font> actual: out.map( &<font color="#ffa0a0">:chomp</font> ),<br /><font color="#ffff00">36 </font> target: [ <font color="#ffdead">"</font><font color="#ffa0a0">42</font><font color="#ffdead">"</font>, <font color="#ffdead">""</font> ]<br /><font color="#ffff00">37 </font><font color="#f0e68c"><b>end</b></font><br /><font color="#ffff00">38 </font><br /><font color="#ffff00">39 </font><font color="#bdb76b"><b>Verify</b></font> <font color="#ffdead">"</font><font color="#ffa0a0">Providing stdin</font><font color="#ffdead">"</font> <font color="#f0e68c"><b>do</b></font><br /><font color="#ffff00">40 </font> with_input <font color="#ffdead">%w{</font><font color="#ffa0a0">The Quick Brown Fox</font><font color="#ffdead">}</font> <font color="#f0e68c"><b>do</b></font><br /><font color="#ffff00">41 </font> verify <font color="#ffdead">%{</font><font color="#ffa0a0">first line is "The"</font><font color="#ffdead">}</font> <font color="#f0e68c"><b>do</b></font><br /><font color="#ffff00">42 </font> <font color="#ffdead">"</font><font color="#ffa0a0">The</font><font color="#ffdead">"</font> == gets.chomp<br /><font color="#ffff00">43 </font> <font color="#f0e68c"><b>end</b></font><br /><font color="#ffff00">44 </font> <font color="#f0e68c"><b>end</b></font><br /><font color="#ffff00">45 </font><font color="#f0e68c"><b>end</b></font><br /><br /></font></div>Robert Doberhttp://www.blogger.com/profile/06304126315464640783noreply@blogger.com0tag:blogger.com,1999:blog-6539205209092092742.post-30464982579261256992009-01-19T15:37:00.063+01:002009-01-26T15:56:20.237+01:00Methodless Ruby<div class="myblog"><br />In an earlier post I have shown how Ruby can be used (or abused) as a language implementing different object models than the built in model. Ruby's built in model is Single Inheritance and Module Mixin as probably everybody reading this knows. If you have stumbled upon this blog and do not know Ruby's object model you might want to read about it first. E.g. in the famous <a href="http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_classes.html">Pickaxe Book</a>.<br /><br />The last time I was exploring object models I blogged about <a href="http://ruby-smalltalk.blogspot.com/2007/08/from-method-redefinition-to-anonymous.html">"Classless Ruby"</a>. <br />I was however still making use of modules and methods.<br /><br />Today I will get rid of them too, well I will still write some Core class methods but once that is done there will be no more classes, no more modules, no more methods and no more instance variables.<br /><br />The outcome will resemble <a href="#did_u_say_prototype">prototypes</a>, but the beauty of the whole, the beauty of Ruby, is that even that is just my implementation and different developement paradigms could be explored almost as easily.<br /><br />'nuff ta'king for now...<br /><br /><hr></hr><br /><!-- h1: TOC --><br /><a name="toc1"></a><br /><span class="headline1">TOC</span><br /><br /><a class="headline1" href="#where_put_behavior">Where To Put The Behavior?</a><div class="tocdesc"> A very <i>revolutionary</i> idea stolen from Perl 5! (<i>five not 120</i>)</div><br /><a class="headline1" href="#behave_or_not">But What Is Behavior, Now?</a><div class="tocdesc"> Hashes might still store proc objects for different purposes though.</div><br /><a class="headline1" href="#fixing">Fixing Some Problems</a><div class="tocdesc">Some of the shortcomings discovered so far can be fixed...</div><br /><a class="headline1" href="#vare">Quinctilie Vare, <span class="stroken">legiones</span> hash redde!</a><sup><a href="http://en.wikipedia.org/wiki/Varus">[1]</a></sup><div class="tocdesc"> I am much luckier than Quinctilius Varus, I <b>can</b> give the hash back.</div><br /><a class="headline1" href="#code_reuse">Codereuse</a><div class="tocdesc">Being lazy I want mixins an that kind of stuff, right!</div><br /><a class="headline1" href="#resume">Résumé</a><div class="tocdesc"> What did I actually do? Where shall I go?</div><br /><br /><hr></hr><br /><br /><a name="where_put_behavior" href="#toc1">Back to TOC</a><br /><div class="headline1">Where To Put Behavior?</div><br />I did not really think a long time about this, and I do not believe that there are many alternatives with the given constraints. Hashes really should do the job nicely.<br />They allow us to store the data and the behavior together ( we are not abandoning the encapsulation principle yet ). As I mentioned above that is exactly what Larry Wall did in Perl 5 and that allowed for some nice object oriented programs after all.<br /><br />A straightforward shot at this might look like the following<br /><pre class="myblog"><font color="#a52a2a"> 1 </font>book = {<br /><font color="#a52a2a"> 2 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">'</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Mark Twain</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">'</font></span>,<br /><font color="#a52a2a"> 3 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span> => <span style="background-color: #f2f2f2"><font color="#ff00ff">2364</font></span>,<br /><font color="#a52a2a"> 4 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:title</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">'</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Tom Sawyer meets Data</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">'</font></span>,<br /><font color="#a52a2a"> 5 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span> => <font color="#a52a2a"><b>proc</b></font>{ | <font color="#008b8b">myself</font> |<br /><font color="#a52a2a"> 6 </font> puts <span style="background-color: #f2f2f2"><font color="#6a5acd">%<</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">"</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:title</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">" by </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff"> was published in </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">></font></span><br /><font color="#a52a2a"> 7 </font>}<br /><font color="#a52a2a"> 8 </font>}<br /><font color="#a52a2a"> 9 </font><br /><font color="#a52a2a">10 </font>book[<span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span>].call( book )<br /><font color="#a52a2a">11 </font><br /></pre><br /><br />When we look at line #10 however we are not very happy, are we? Well we should not. There are two redundancies involved. Firstly we have to pass the receiver into the proc object again, and secondly we have to call the proc object recursively. We will get rid of this nuisance immediately.<br /><br /><pre class="myblog"><font color="#a52a2a">1 </font><font color="#a020f0">class</font> <font color="#2e8b57"><b>Hash</b></font><br /><font color="#a52a2a">2 </font> alias_method <span style="background-color: #f2f2f2"><font color="#ff00ff">:__old_get__</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:[]</font></span><br /><font color="#a52a2a">3 </font> <font color="#a020f0">def</font> <font color="#008b8b">[]</font> name, *args<br /><font color="#a52a2a">4 </font> value = __old_get__( name )<br /><font color="#a52a2a">5 </font> <font color="#a52a2a"><b>return</b></font> value <font color="#a52a2a"><b>unless</b></font> value && value.is_a? <font color="#2e8b57"><b>Proc</b></font><br /><font color="#a52a2a">6 </font> value.call( <span style="background-color: #f2f2f2"><font color="#ff00ff">self</font></span>, *args )<br /><font color="#a52a2a">7 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a">8 </font><font color="#a020f0">end</font><br /></pre><br /><br />and now this works<br /><br /><pre class="myblog"><font color="#a52a2a">1 </font>book[<span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span>] <font color="#0000ff"># --> "Tom Sawyer meets Data" by Mark Twain was published in 2364</font><br /></pre><br /><br /><br /><hr></hr><br /><a name="behave_or_not" href="#toc1">Back to TOC</a><br /><div class="headline1">But What Is Behavior, Now?</div><br /><br />We are very, very happy with our design, we can pass arguments into our new <code>[]</code> call syntax as the following example shows:<br /><pre class="myblog"><font color="#a52a2a"> 1 </font>book = {<br /><font color="#a52a2a"> 2 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">'</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Mark Twain</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">'</font></span>,<br /><font color="#a52a2a"> 3 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span> => <span style="background-color: #f2f2f2"><font color="#ff00ff">2364</font></span>,<br /><font color="#a52a2a"> 4 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:title</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">'</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Tom Sawyer meets Data</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">'</font></span>,<br /><font color="#a52a2a"> 5 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span> => <font color="#a52a2a"><b>proc</b></font>{ | <font color="#008b8b">myself</font> |<br /><font color="#a52a2a"> 6 </font> puts <span style="background-color: #f2f2f2"><font color="#6a5acd">%<</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">"</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:title</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">" by </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff"> was published in </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><br /><font color="#a52a2a"> 7 </font><span style="background-color: #f2f2f2"><font color="#ff00ff"> annotations: </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>(myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span>]||[]).join(<span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">, </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>)<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">></font></span><br /><font color="#a52a2a"> 8 </font> },<br /><font color="#a52a2a"> 9 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:add_annotations</font></span> => <font color="#a52a2a"><b>proc</b></font>{ |<font color="#008b8b">myself</font>, *<font color="#008b8b">annotations</font>|<br /><font color="#a52a2a">10 </font> myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span> ] ||= []<br /><font color="#a52a2a">11 </font> myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span> ] += annotations<br /><font color="#a52a2a">12 </font> }<br /><font color="#a52a2a">13 </font><br /><font color="#a52a2a">14 </font>}<br /><font color="#a52a2a">15 </font><br /><font color="#a52a2a">16 </font>book[<span style="background-color: #f2f2f2"><font color="#ff00ff">:add_annotations</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Edited by J-L.Picard</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Reviewed by William T. Riker</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>]<br /><font color="#a52a2a">17 </font>book[<span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span>]<br /></pre><br /><br />and we get the hoped for<br /><pre><br />"Tom Sawyer meets Data" by Mark Twain was published in 2364<br /> annotations: Edited by J-L.Picard, Reviewed by William T. Riker<br /></pre><br /><br />Thus I am happy again, and often when I am happy, there is something really, really wrong. One of the things that are wrong became apparent when I needed to store a proc object in the hash and that proc object was not implementing behavior of the object.<br /><pre class="myblog"><font color="#a52a2a">1 </font>book[<span style="background-color: #f2f2f2"><font color="#ff00ff">:adder</font></span>] = <font color="#a52a2a"><b>proc</b></font>{ | <font color="#008b8b">a</font>, <font color="#008b8b">b</font> | a + b }<br /><font color="#a52a2a">2 </font>a = book[<span style="background-color: #f2f2f2"><font color="#ff00ff">:adder</font></span>]<br /></pre><br /><br />When running this I got of course an error<br /><pre><br />method-less-4.rb:34:in `block in <main>': undefined method `+' for #<Hash:0x9841e7c> (NoMethodError)<br /> from method-less-4.rb:10:in `call'<br /> from method-less-4.rb:10:in `[]'<br /> from method-less-4.rb:35:in `<main>'<br /></pre> <br /><br /><hr></hr><br /><br /><a name="fixing" href="#toc1">Back to TOC</a><br /><div class="headline1">Fixing Some Problems</div><br />So far we have successfully bound behavior to data with procs. We have however introduced an ambiguity between <i>Behavior</i> members and <i>Proc</i> members in our objects.<br />Furthermore we have not yet introduced a generic object creation mechanism. No, <code>dup</code> will not suffice, but there is not needed much more.<br />Worse though, there is no object initialization mechanism either. We will fix these three issues now and then review what we have got, again.<br /><br /><b>Behavior is not Proc</b><br />Subclassing Proc shall give us the information we need to do the <i>right</i> thing when accessing an object's member. Furthermore a nice subclass factory method, e.g. <code>Kernel#behavior</code> will provide more readable code.<br /><br /><pre class="myblog"><font color="#a52a2a"> 1 </font><br /><font color="#a52a2a"> 2 </font><font color="#2e8b57"><b>Behavior</b></font> = <font color="#2e8b57"><b>Class</b></font>::new <font color="#2e8b57"><b>Proc</b></font><br /><font color="#a52a2a"> 3 </font><br /><font color="#a52a2a"> 4 </font><font color="#a020f0">module</font> <font color="#2e8b57"><b>Kernel</b></font><br /><font color="#a52a2a"> 5 </font> <font color="#a020f0">def</font> <font color="#008b8b">behavior</font> &blk<br /><font color="#a52a2a"> 6 </font> <font color="#2e8b57"><b>Behavior</b></font>::new( &blk )<br /><font color="#a52a2a"> 7 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a"> 8 </font><font color="#a020f0">end</font><br /><font color="#a52a2a"> 9 </font><br /></pre><br />In lines 1 to 8 we just subclass Proc and define a Kernel method that wraps the constructor call of Behavior which is the inherited one. <br /><pre class="myblog"><font color="#a52a2a"> 9 </font><br /><font color="#a52a2a">10 </font><font color="#a020f0">class</font> <font color="#2e8b57"><b>Hash</b></font><br /><font color="#a52a2a">11 </font> alias_method <span style="background-color: #f2f2f2"><font color="#ff00ff">:__old_get__</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:[]</font></span><br /><font color="#a52a2a">12 </font> <font color="#a020f0">def</font> <font color="#008b8b">[]</font> name, *args<br /><font color="#a52a2a">13 </font> value = __old_get__( name )<br /><font color="#a52a2a">14 </font> <font color="#a52a2a"><b>return</b></font> value <font color="#a52a2a"><b>unless</b></font> value && value.is_a?( <font color="#2e8b57"><b>Behavior</b></font> )<br /><font color="#a52a2a">15 </font> value.call( <span style="background-color: #f2f2f2"><font color="#ff00ff">self</font></span>, *args )<br /><font color="#a52a2a">16 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a">17 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">18 </font><br /></pre><br />Thanks to our little sub-classing game before we can dispatch on Behavior now. By specifying Behavior with the <code>behavior</code> method, and Proces with the <code>proc</code> method (see lines 23, 27 & 31) we implement the desired behavior ( no pun intended ).<br /><pre class="myblog"><br /><font color="#a52a2a">18 </font><br /><font color="#a52a2a">19 </font>book = {<br /><font color="#a52a2a">20 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">'</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Mark Twain</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">'</font></span>,<br /><font color="#a52a2a">21 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span> => <span style="background-color: #f2f2f2"><font color="#ff00ff">2364</font></span>,<br /><font color="#a52a2a">22 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:title</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">'</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Tom Sawyer meets Data</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">'</font></span>,<br /><font color="#a52a2a">23 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span> => <b>behavior</b>{ | <font color="#008b8b">myself</font> |<br /><font color="#a52a2a">24 </font> puts <span style="background-color: #f2f2f2"><font color="#6a5acd">%<</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">"</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:title</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">" by </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff"> was published in </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><br /><font color="#a52a2a">25 </font><span style="background-color: #f2f2f2"><font color="#ff00ff"> annotations: </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>(myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span>]||[]).join(<span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">, </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>)<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">></font></span><br /><font color="#a52a2a">26 </font> },<br /><font color="#a52a2a">27 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:add_annotations</font></span> => <b>behavior</b>{ |<font color="#008b8b">myself</font>, *<font color="#008b8b">annotations</font>|<br /><font color="#a52a2a">28 </font> myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span> ] ||= []<br /><font color="#a52a2a">29 </font> myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span> ] += annotations<br /><font color="#a52a2a">30 </font> },<br /><font color="#a52a2a">31 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:adder</font></span> => <font color="#a52a2a"><b>proc</b></font>{ | <font color="#008b8b">a</font>, <font color="#008b8b">b</font> | a + b }<br /><font color="#a52a2a">32 </font>}<br /><font color="#a52a2a">33 </font><br /><font color="#a52a2a">34 </font>book[<span style="background-color: #f2f2f2"><font color="#ff00ff">:add_annotations</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Edited by J-L.Picard</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Reviewed by William T. Riker</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>]<br /><font color="#a52a2a">35 </font>book[<span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span>]<br /><font color="#a52a2a">36 </font><br /><font color="#a52a2a">37 </font><br /><font color="#a52a2a">38 </font>book[<span style="background-color: #f2f2f2"><font color="#ff00ff">:adder</font></span>] = <font color="#a52a2a"><b>proc</b></font>{ | <font color="#008b8b">a</font>, <font color="#008b8b">b</font> | a + b }<br /><font color="#a52a2a">39 </font>a = book[<span style="background-color: #f2f2f2"><font color="#ff00ff">:adder</font></span>]<br /><font color="#a52a2a">40 </font>p a.call( <span style="background-color: #f2f2f2"><font color="#ff00ff">40</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">2</font></span> )<br /></pre><br />and we get<br /><pre><br />"Tom Sawyer meets Data" by Mark Twain was published in 2364<br /> annotations: Edited by J-L.Picard, Reviewed by William T. Riker<br />42<br /></pre><br /><br /><b>Constructing and Initializing Objects</b><br />In order to construct objects we have to make sure that they do not share any data with the <i>prototype</i>. This holds even more as, in this kind of prototype programming <i>any</i> object can be used as the receiver of the construction message.<br /><pre class="myblog"><font color="#a52a2a">1 </font><font color="#a020f0">class</font> <font color="#2e8b57"><b>Hash</b></font><br /><font color="#a52a2a">2 </font> <font color="#a020f0">def</font> <font color="#008b8b">new</font> initial_values = {}<br /><font color="#a52a2a">3 </font> dup<br /><font color="#a52a2a">4 </font> initial_values.inject( dup ){ | <font color="#008b8b">o</font>, (<font color="#008b8b">k</font>, <font color="#008b8b">v</font>) |<br /><font color="#a52a2a">5 </font> o.update k => ( v.dup <font color="#a52a2a"><b>rescue</b></font> v )<br /><font color="#a52a2a">6 </font> }<br /><font color="#a52a2a">7 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a">8 </font><font color="#a020f0">end</font><br /></pre><br />In the code above we just define a <code>new</code> method for a hash. N.B. This is an instance method and must not be confused with Hashes class method. Maybe a different name would have been in order, I honestly do not know. We duplicate the receiver and inject the initial values hash. That can than be used as follows.<br /><pre class="myblog"><font color="#a52a2a"> 1 </font>book = {<br /><font color="#a52a2a"> 2 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span> => behavior{ | <font color="#008b8b">myself</font> |<br /><font color="#a52a2a"> 3 </font> puts <span style="background-color: #f2f2f2"><font color="#6a5acd">%<</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">"</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:title</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">" by </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff"> was published in </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><br /><font color="#a52a2a"> 4 </font><span style="background-color: #f2f2f2"><font color="#ff00ff"> annotations: </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>(myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span>]||[]).join(<span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">, </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>)<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">></font></span><br /><font color="#a52a2a"> 5 </font> },<br /><font color="#a52a2a"> 6 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:add_annotations</font></span> => behavior{ |<font color="#008b8b">myself</font>, *<font color="#008b8b">annotations</font>|<br /><font color="#a52a2a"> 7 </font> myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span> ] ||= []<br /><font color="#a52a2a"> 8 </font> myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span> ] += annotations<br /><font color="#a52a2a"> 9 </font> }<br /><font color="#a52a2a">10 </font>}<br /><font color="#a52a2a">11 </font><br /><font color="#a52a2a">12 </font>i_robot = book.new( <span style="background-color: #f2f2f2"><font color="#ff00ff">:title</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">I Robot</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Isaac Asimov</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span> => <span style="background-color: #f2f2f2"><font color="#ff00ff">1950</font></span> )<br /><font color="#a52a2a">13 </font>pickaxe = book.new( <span style="background-color: #f2f2f2"><font color="#ff00ff">:title</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Programming Ruby 1.9</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Dave Thomas</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">2009</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span> )<br /><font color="#a52a2a">14 </font>pickaxe[<span style="background-color: #f2f2f2"><font color="#ff00ff">:add_annotations</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Live Long and Ruby!</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">and there was Andy Hunt</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">... and Chad Fowler</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span> ]<br /><font color="#a52a2a">15 </font><br /><font color="#a52a2a">16 </font>i_robot[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span> ]<br /><font color="#a52a2a">17 </font>pickaxe[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span> ]<br /></pre><br /><br />which duly produces the following output:<br /><pre class="myblog"><br />"I Robot" by Isaac Asimov was published in 1950<br /> annotations: <br />"Programming Ruby 1.9" by Dave Thomas was published in 2009<br /> annotations: Live Long and Ruby!, and there was Andy Hunt, ... and Chad Fowler<br /></pre><br /><br />Please also note that we could have written <code>pickaxe = i_robot.new(...)</code> in line #13 above, without any change in the semantics of the program.<br /><br /><a name="did_u_say_prototype"></a><br /><b>Did I say Prototype?</b><br /><br />I have used the term <i>prototype</i> rather nonchalantly so far. Maybe it is time to define a bit better what <i>I</i> understand under this term in this context. <br /><br />A <i>prototype</i> is an object oriented model in which the behavior of any object can be shared by using this very object as a <i>prototype</i>. This is a little more general than e.g. the Javascript <i>prototype</i> model in which each object simply has a <i>prototype</i> and this prototype cannot be used as a member of the set of objects it defines.<br />In our case however that is perfectly possible.<br />That is the reason why the above remark about line #13 holds: Either <code>book</code> or <code>i_robot</code> can be used as a prototype, although they are perfectly members of the set of objects defined by the prototype.<br /><br /><b>Criticisms</b><br />Although I find it very amusing, amazing and instructive what I have done here I have to get my feet back on the ground again.<br /><br />This design suffers from some serious drawbacks.<br /><ul><br /> <li>All members are accessible.</li><br /> <li>Calling behavior with blocks has a clumsy syntax (namely <code>send :[],... do end</code>).</li><br /> <li>Name Errors (e.g. behavior names misspelled ) are completely obfuscated.</li><br /> <li>There is one only constructor, it does not allow for different behavior depending on the prototype.</li><br /> <li>Last but not least, we have monopolized hashes. And that of course is completely inacceptable.</li><br /></ul><br />The public accessibility of all members is somehow not really an issue in some application contexts. The clumsy syntax can be worked around and probably the naming problem can be resolved by a different implementation of <code>Hash#[]</code> e.g. using #fetch. <br />However, the last two points are inacceptable.<br /><br /><hr></hr><br /><br /><a name="vare" href="#toc1">Back to TOC</a><br /><div class="headline1">Quinctilie Vare, <span class="stroken">legiones</span> hash redde!</div><br /><br />Our first duty to the emperor Ruby is to give her her hashes back. I have lost them in the quest for new territory for the Ruby empire but luckily for me I can give them back (for a - however very little - prize ).<br /><br />The solution of course is not to use instances of <code>Hash</code> as our <i>Object -- Behavior</i> glue. Well actually we still will use hashes, but specialized hashes, a lession learned from our <code>Behavior</code> class. Well let's do it:<br /><pre class="myblog"><font color="#a52a2a"> 1 </font><br /><font color="#a52a2a"> 2 </font><font color="#a020f0">class</font> <font color="#2e8b57"><b>BHash</b></font> < <font color="#2e8b57"><b>Hash</b></font><br /><font color="#a52a2a"> 3 </font> <font color="#a020f0">def</font> <font color="#008b8b">[]</font> name, *args<br /><font color="#a52a2a"> 4 </font> value = <font color="#a52a2a"><b>super</b></font>( name )<br /><font color="#a52a2a"> 5 </font> <font color="#a52a2a"><b>return</b></font> value <font color="#a52a2a"><b>unless</b></font> value && value.is_a?( <font color="#2e8b57"><b>Behavior</b></font> )<br /><font color="#a52a2a"> 6 </font> value.call( <span style="background-color: #f2f2f2"><font color="#ff00ff">self</font></span>, *args )<br /><font color="#a52a2a"> 7 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a"> 8 </font> <font color="#a020f0">def</font> <font color="#008b8b">new</font> *args<br /><font color="#a52a2a"> 9 </font> dup.tap <font color="#a52a2a"><b>do</b></font> | <font color="#008b8b">o</font> |<br /><font color="#a52a2a">10 </font> o[<span style="background-color: #f2f2f2"><font color="#ff00ff">:init</font></span>, *args] <font color="#a52a2a"><b>if</b></font> o.fetch( <span style="background-color: #f2f2f2"><font color="#ff00ff">:init</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">nil</font></span> ).is_a? <font color="#2e8b57"><b>Behavior</b></font> <br /><font color="#a52a2a">11 </font> <font color="#a52a2a"><b>end</b></font><br /><font color="#a52a2a">12 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a">13 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">14 </font><br /></pre><br /><br />First we have defined our subclass of <code>Hash</code> called <code>BHash</code> (for Behavior Hash). We can find the redefinition of the <code>#[]</code> method again and the generic constructor. Thus we have freed the core Hash class from all our <i>achievements</i>. Please do note that some other issues could be handled here easily now, as e.g. raising a <code>NameError</code> for access to undefined keys. This would make the code even longer though and not be of much technical interest here.<br /><pre class="myblog"><br /><font color="#a52a2a">14 </font><br /><font color="#a52a2a">15 </font><font color="#a020f0">class</font> <font color="#2e8b57"><b>Hash</b></font><br /><font color="#a52a2a">16 </font> <font color="#a020f0">def</font> <font color="#008b8b">to_bhash</font><br /><font color="#a52a2a">17 </font> <font color="#2e8b57"><b>BHash</b></font>::new.update <span style="background-color: #f2f2f2"><font color="#ff00ff">self</font></span><br /><font color="#a52a2a">18 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a">19 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">20 </font><br /><font color="#a52a2a">21 </font><font color="#a020f0">module</font> <font color="#2e8b57"><b>Kernel</b></font><br /><font color="#a52a2a">22 </font> <font color="#a020f0">def</font> <font color="#008b8b">object</font> a_hash<br /><font color="#a52a2a">23 </font> a_hash.to_bhash<br /><font color="#a52a2a">24 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a">25 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">26 </font><br /></pre><br />In the next block of code we have handled the issue I had mentioned above, the <i>small prize</i> to pay for our sub-classing. This prize might even be a blessing in disguise because it frees the hash literal syntax, well for, hash literals.<br />Thus first I defined a convenient conversion method, that creates a <code>BHash</code> out from a <code>Hash</code> and then I provided a wrapper method in <code>Kernel</code> called <code>object</code>. This changes the look of file of our object definitions a little bit.<br /><pre class="myblog"><br /><font color="#a52a2a">26 </font><br /><font color="#a52a2a">27 </font>book = object <span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span> => behavior{ | <font color="#008b8b">myself</font> |<br /><font color="#a52a2a">28 </font> puts <span style="background-color: #f2f2f2"><font color="#6a5acd">%<</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">"</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:title</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">" by </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff"> was published in </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><br /><font color="#a52a2a">29 </font><span style="background-color: #f2f2f2"><font color="#ff00ff"> annotations: </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>(myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span>]||[]).join(<span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">, </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>)<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">></font></span><br /><font color="#a52a2a">30 </font> },<br /><font color="#a52a2a">31 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:add_annotations</font></span> => behavior{ | <font color="#008b8b">myself</font>, *<font color="#008b8b">annotations</font> |<br /><font color="#a52a2a">32 </font> myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span> ] ||= []<br /><font color="#a52a2a">33 </font> myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span> ] += annotations<br /><font color="#a52a2a">34 </font> },<br /><font color="#a52a2a">35 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:init</font></span> => behavior{ | <font color="#008b8b">myself</font>, <font color="#008b8b">title</font>, <font color="#008b8b">others</font>={} |<br /><font color="#a52a2a">36 </font> myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:title</font></span> ] = title<br /><font color="#a52a2a">37 </font> myself.update others<br /><font color="#a52a2a">38 </font> }<br /><font color="#a52a2a">39 </font><br /><font color="#a52a2a">40 </font><br /><font color="#a52a2a">41 </font>gray = book.new( <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">The Picture of Dorian Gray</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Oscar Wilde</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span> => <span style="background-color: #f2f2f2"><font color="#ff00ff">1890</font></span> )<br /><font color="#a52a2a">42 </font>pickaxe = book.new( <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Programming Ruby 1.9</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Dave Thomas</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span> => <span style="background-color: #f2f2f2"><font color="#ff00ff">2009</font></span> )<br /><font color="#a52a2a">43 </font>pickaxe[<span style="background-color: #f2f2f2"><font color="#ff00ff">:add_annotations</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Live Long and Ruby!</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">and there was Andy Hunt</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">... and Chad Fowler</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span> ]<br /><font color="#a52a2a">44 </font><br /><font color="#a52a2a">45 </font>gray[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span> ]<br /><font color="#a52a2a">46 </font>pickaxe[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span> ]<br /></pre><br /><br />And this code is satisfying our conditions above and produces the expected output:<br /><br /><pre><br />"The Picture of Dorian Gray" by Oscar Wilde was published in 1890<br /> annotations: <br />"Programming Ruby 1.9" by Dave Thomas was published in 2009<br /> annotations: Live Long and Ruby!, and there was Andy Hunt, ... and Chad Fowler<br /></pre><br /><!--<br />Click on the following link if you want to have a look at the complete implementation.<br /><a href="javascript:toggle_id('hidden_code1');">Toggle code visibility</a><br />--><br /><!-- Hidden --><br /><div id="hidden_code1"><br /><pre class="myblog"><font color="#a52a2a"> 1 </font><font color="#2e8b57"><b>Behavior</b></font> = <font color="#2e8b57"><b>Class</b></font>::new <font color="#2e8b57"><b>Proc</b></font><br /><font color="#a52a2a"> 2 </font><br /><font color="#a52a2a"> 3 </font><font color="#a020f0">module</font> <font color="#2e8b57"><b>Kernel</b></font><br /><font color="#a52a2a"> 4 </font> <font color="#a020f0">def</font> <font color="#008b8b">behavior</font> &blk<br /><font color="#a52a2a"> 5 </font> <font color="#2e8b57"><b>Behavior</b></font>::new( &blk )<br /><font color="#a52a2a"> 6 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a"> 7 </font> <font color="#a020f0">def</font> <font color="#008b8b">object</font> a_hash<br /><font color="#a52a2a"> 8 </font> a_hash.to_bhash<br /><font color="#a52a2a"> 9 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a">10 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">11 </font><br /><font color="#a52a2a">12 </font><font color="#a020f0">class</font> <font color="#2e8b57"><b>BHash</b></font> < <font color="#2e8b57"><b>Hash</b></font><br /><font color="#a52a2a">13 </font> <font color="#a020f0">def</font> <font color="#008b8b">[]</font> name, *args<br /><font color="#a52a2a">14 </font> value = <font color="#a52a2a"><b>super</b></font>( name )<br /><font color="#a52a2a">15 </font> <font color="#a52a2a"><b>return</b></font> value <font color="#a52a2a"><b>unless</b></font> value && value.is_a?( <font color="#2e8b57"><b>Behavior</b></font> )<br /><font color="#a52a2a">16 </font> value.call( <span style="background-color: #f2f2f2"><font color="#ff00ff">self</font></span>, *args )<br /><font color="#a52a2a">17 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a">18 </font> <font color="#a020f0">def</font> <font color="#008b8b">new</font> *args<br /><font color="#a52a2a">19 </font> dup.tap <font color="#a52a2a"><b>do</b></font> | <font color="#008b8b">o</font> |<br /><font color="#a52a2a">20 </font> o[<span style="background-color: #f2f2f2"><font color="#ff00ff">:init</font></span>, *args] <font color="#a52a2a"><b>if</b></font> o.fetch( <span style="background-color: #f2f2f2"><font color="#ff00ff">:init</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">nil</font></span> ).is_a? <font color="#2e8b57"><b>Behavior</b></font> <br /><font color="#a52a2a">21 </font> <font color="#a52a2a"><b>end</b></font><br /><font color="#a52a2a">22 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a">23 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">24 </font><br /><font color="#a52a2a">25 </font><font color="#a020f0">class</font> <font color="#2e8b57"><b>Hash</b></font><br /><font color="#a52a2a">26 </font> <font color="#a020f0">def</font> <font color="#008b8b">to_bhash</font><br /><font color="#a52a2a">27 </font> <font color="#2e8b57"><b>BHash</b></font>::new.update <span style="background-color: #f2f2f2"><font color="#ff00ff">self</font></span><br /><font color="#a52a2a">28 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a">29 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">30 </font>book = object <span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span> => behavior{ | <font color="#008b8b">myself</font> |<br /><font color="#a52a2a">31 </font> puts <span style="background-color: #f2f2f2"><font color="#6a5acd">%<</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">"</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:title</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">" by </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff"> was published in </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span>]<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><br /><font color="#a52a2a">32 </font><span style="background-color: #f2f2f2"><font color="#ff00ff"> annotations: </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>(myself[<span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span>]||[]).join(<span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">, </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>)<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">></font></span><br /><font color="#a52a2a">33 </font> },<br /><font color="#a52a2a">34 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:add_annotations</font></span> => behavior{ | <font color="#008b8b">myself</font>, *<font color="#008b8b">annotations</font> |<br /><font color="#a52a2a">35 </font> myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span> ] ||= []<br /><font color="#a52a2a">36 </font> myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:annotations</font></span> ] += annotations<br /><font color="#a52a2a">37 </font> },<br /><font color="#a52a2a">38 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:init</font></span> => behavior{ | <font color="#008b8b">myself</font>, <font color="#008b8b">title</font>, <font color="#008b8b">others</font>={} |<br /><font color="#a52a2a">39 </font> myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:title</font></span> ] = title<br /><font color="#a52a2a">40 </font> myself.update others<br /><font color="#a52a2a">41 </font> }<br /><font color="#a52a2a">42 </font><br /><font color="#a52a2a">43 </font><br /><font color="#a52a2a">44 </font>gray = book.new( <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">The Picture of Dorian Gray</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Oscar Wilde</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span> => <span style="background-color: #f2f2f2"><font color="#ff00ff">1890</font></span> )<br /><font color="#a52a2a">45 </font>pickaxe = book.new( <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Programming Ruby 1.9</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Dave Thomas</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span> => <span style="background-color: #f2f2f2"><font color="#ff00ff">2009</font></span> )<br /><font color="#a52a2a">46 </font>pickaxe[<span style="background-color: #f2f2f2"><font color="#ff00ff">:add_annotations</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Live Long and Ruby!</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">and there was Andy Hunt</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">... and Chad Fowler</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span> ]<br /><font color="#a52a2a">47 </font><br /><font color="#a52a2a">48 </font>gray[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span> ]<br /><font color="#a52a2a">49 </font>pickaxe[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span> ]<br /><font color="#a52a2a">50 </font><br /></pre><br /><!-- end hidden --><br /></div><br /><br /><hr></hr><br /><a name="code_reuse" href="#toc1">Back to TOC</a><br /><div class="headline1">Code Reuse</div><br /><br />Well we are all lazy, it's almost a hype ;). Seriously there is one feature we might miss at first sight, that of mixins. On second sight of course and knowing that we use a subclass of Hash as our behavior and state container we see that we can simply update the container with different behavior. A naive, but perfectly working approach would be to do the following:<br /><!-- <br /> Code Reuse <br />--><br /><pre class="myblog"><font color="#a52a2a">1 </font><font color="#a020f0">class</font> <font color="#2e8b57"><b>BHash</b></font> < <font color="#2e8b57"><b>Hash</b></font><br /><font color="#a52a2a">2 </font> <font color="#a020f0">def</font> <font color="#008b8b">add_behavior</font> behavior<br /><font color="#a52a2a">3 </font> <font color="#a52a2a"><b>abort</b></font> <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Not a behavior </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>behavior<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span> <font color="#a52a2a"><b>unless</b></font> <br /><font color="#a52a2a">4 </font> behavior.is_a? <span style="background-color: #f2f2f2"><font color="#ff00ff">self</font></span>.class <font color="#0000ff"># a tailormade exception would be raised in production code, of course</font><br /><font color="#a52a2a">5 </font> update behavior<br /><font color="#a52a2a">6 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a">7 </font><font color="#a020f0">end</font><br /></pre><br /><br />Given this primitive wrapper around <code>Hash#update</code> we can now <i>extend</i> our prototypes / objects as in the following code:<br /><pre class="myblog"><font color="#a52a2a"> 1 </font>isbn = object <span style="background-color: #f2f2f2"><font color="#ff00ff">:set_isbn</font></span> => behavior { |<font color="#008b8b">myself</font>, <font color="#008b8b">isbn</font> | myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:isbn</font></span> ] = myself[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:add_isdn_check</font></span>, isbn ] },<br /><font color="#a52a2a"> 2 </font> <span style="background-color: #f2f2f2"><font color="#ff00ff">:add_isdn_check</font></span> => behavior { | <font color="#008b8b">_</font>, <font color="#008b8b">isbn</font> | <br /><font color="#a52a2a"> 3 </font> <font color="#0000ff"># begin of boring cksum code</font><br /><font color="#a52a2a"> 4 </font> isbn + <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">*</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><br /><font color="#a52a2a"> 5 </font> <font color="#0000ff"># end of boring chksum code</font><br /><font color="#a52a2a"> 6 </font> }<br /><font color="#a52a2a"> 7 </font><br /><font color="#a52a2a"> 8 </font>book.add_behavior isbn<br /><font color="#a52a2a"> 9 </font>hegel = book.new( <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Hegel's Philosophy of Reality</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:author</font></span> => <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">Robert M. Wallace</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">:date</font></span> => <span style="background-color: #f2f2f2"><font color="#ff00ff">2005</font></span> )<br /><font color="#a52a2a">10 </font>hegel[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:set_isbn</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">521844843</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span> ]<br /><font color="#a52a2a">11 </font>hegel[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:image</font></span> ]<br /><font color="#a52a2a">12 </font>puts hegel[ <span style="background-color: #f2f2f2"><font color="#ff00ff">:isbn</font></span> ]<br /><font color="#a52a2a">13 </font><br /></pre><br />printing <br /><pre><br />"Hegel's Philosophy of Reality" by Robert M. Wallace was published in 2005<br /> annotations: <br />521844843*<br /></pre><br /><br /> <br /><hr></hr><br /><a name="resume" href="#toc1">Back to TOC</a><br /><div class="headline1">Résumé</div><br /><br />I have now implemented the basic devices needed for prototype based programming, and I am asking myself, <i>What now?</i>. As a matter of fact I am looking back on the code I have written I am missing some properties badly:<br /><ul><br /> <li>Call syntax is clumsy for blocks.</li><br /> <li>Code reuse does not allow access to overridden behavior.</li><br /> <li>Data and behavior are mixed.</li><br /> <li>Data and behavior is public.</li><br /></ul><br />Now I am sure, that all of these matters can be fixed, but shall this be done with hashes as the basic device?<br /><br />I am really not sure and there is just one way to find out, implementing some of it and playing around with it...<br /><br />And that is what I will be doing now. Thanks for having stepped by.<br /></div> <!-- class="myblog" -->Robert Doberhttp://www.blogger.com/profile/06304126315464640783noreply@blogger.com2tag:blogger.com,1999:blog-6539205209092092742.post-37707219999359138452009-01-07T13:33:00.084+01:002009-01-19T17:10:14.833+01:00Streams, Lazy Programs for Lazy Programmers.<div class="myblog"><br />Streams are nothing new at all. Furthermore they have been introduced at least once into the Ruby World on James' excellent blog, <a href="http://blog.grayproductions.net/articles/infinite_streams">Shades of Gray</a>, with which many of the Ruby community are familiar, at least I hope ;). I can only encourage you to read the whole <a href="http://blog.grayproductions.net/categories/higherorder_ruby">High Order Ruby</a> series. For those looking for more reading on this topic the following link provides a gold mine; <a href="http://hop.perl.plover.com/book/">High Order Perl</a>.<br /><br />Although I had read James' blog entry about two years ago and was fascinated by it, I somehow never had the time to play around with streams. It took a second motivation which I found in a remarkable site: <a href="http://groups.csail.mit.edu/mac/classes/6.001/abelson-sussman-lectures/">Structure and Interpretation of Computer Programs, Video Lecture</a>. This was an absolute treasure for me. Looking at Abelson's presentation of <i>Streams</i> I really wanted to have that in Ruby.<br /><br />For those who do not like my blah, blah, and that is probably about everybody save YHS, but ok it is <b>my</b> Blog after all, I have compiled a small TOC so that they do not have to read the dull stuff while I can still write it, yeah!<br /><br /><hr></hr><br /><!-- h1: TOC --><br /><a name="toc"></a><br /><span class="headline1">TOC</span><br /><br /><a class="headline1" href="#streams_in_ruby">Streams in Ruby</a><div class="tocdesc"> A <i>verbose</i> introduction why, how, when I did what and why, how and when I did not do other things.</div><br /><a class="headline1" href="#diving_into_streams">Diving into Streams</a><div class="tocdesc"> A step by step introduction into streams and their implementation in Ruby.</div><br /><a class="headline1" href="#ruby_meets_streams">Ruby Meets Streams</a><div class="tocdesc">Enumerable Ruby Objects can easily be seen as streams too.</div><br /><a class="headline1" href="#streams_applications">Streams' Applications</a><div class="tocdesc">And yes of course they do serve.</div><br /><br /><hr></hr><br /><br /><a name="streams_in_ruby" href="#toc">Back to TOC</a><br /><span class="headline1">Streams in Ruby</span><br /><br />A stream is a lazy, potentially endless data structure. In Scheme a stream is constructed with the <code>cons-stream</code> special form. As an example Abelson constructs the stream of all integers as follows:<br /><pre class="myblog scheme"><span style="font-family:monospace;"><span style="color: rgb(165, 42, 42);">1 </span><span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">(</span></span><span style="color: rgb(165, 42, 42);"><b>define</b></span> <span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">(</span></span>integers-from start<span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">)</span></span><br /><span style="color: rgb(165, 42, 42);">2 </span> <span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">(</span></span>cons-stream start <span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">(</span></span>integers-from <span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">(</span></span><span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(255, 0, 255);">1+</span></span> start<span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">)</span></span> <span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">)</span></span> <span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">)</span></span><br /><span style="color: rgb(165, 42, 42);">3 </span><span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">)</span></span><br /><span style="color: rgb(165, 42, 42);">4 </span><span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">(</span></span><span style="color: rgb(165, 42, 42);"><b>define</b></span> <span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">(</span></span>integers<span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">)</span></span><br /><span style="color: rgb(165, 42, 42);">5 </span> <span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">(</span></span>integers-from <span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(255, 0, 255);">0</span></span><span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">)</span></span><br /><span style="color: rgb(165, 42, 42);">6 </span><span style="background-color: rgb(242, 242, 242);"><span style="color: rgb(106, 90, 205);">)</span></span></span></pre><br />As such the <code>cons-stream</code> form constructs a stream by creating a <i>Lisp cons cell</i> with a <code>car</code> that consists of an already computed value, called <i>head</i> and a <code>cdr</code> that contains a <i><b>promise</b></i> to compute a new <i>head</i> and a new <i>promise</i>.<br /><br />A practical side note: This was the most difficult thing to remember for me. The concept seems simple enough, but whenever I had a problem in my code I had forgotten that the <i>promise</i> has to return a new <i>stream</i> and not just a new <i>head</i>. For me the following two invariants on streams were the absolute cornerstone for understanding them:<br /><ul><li>While constructing a <i>stream</i> the tail is a <i>promise</i> and thus <span style="font-weight: bold;">not</span> evaluated.<br /></li><li>The <i>tail</i> of a <i>stream</i> will <b>always</b> yield a <i>stream</i> again, potentially the <i>empty stream</i>.<br /></li></ul>Once these two rules sank into my brain it was amazing how easy stream manipulation can become.<br /><br />Given the first of the two invariants above it is quite clear how to specify a<br /><span style="font-style: italic;">promise</span> in Ruby, with code blocks.<br />We can therefore allready imagine how a <code>cons_stream</code> method will look like in Ruby.<br /><pre class="myblog"><span style="color: rgb(165, 42, 42);">1 </span><span style="color: rgb(160, 32, 240);">def</span> <span style="color: rgb(0, 139, 139);">cons_stream</span> head, &tail<br /><span style="color: rgb(165, 42, 42);">2 </span> <span style="color: rgb(0, 0, 255);"># . . .</span><br /><span style="color: rgb(165, 42, 42);">3 </span><span style="color: rgb(160, 32, 240);">end</span></pre>The result of the cons_stream call of course is an object implementing <i>Streams</i> in Ruby. This can easily be verified on the command line:<br /><pre class="myblog bash">ruby -rlab419/functional/streams -e 'include Lab419::SimpleStream;p cons_stream( 42 ){ EmptyStream }'<br />#<Lab419::SimpleStream::Stream:0xb7d85664 @tail=#<Proc:0xb7d8572c@-e:1>, @head=42></pre><br />We can very well see that the <i>promise</i> is stored as a proc object and the head as an actual value.<br /><br />Here however I will take a different path then James took, and I encourage you (again) to read his blog if you prefer the more idiomatic Ruby way to look at streams. I however really wanted<br />to emphasize on the <i>abstraction</i> streams constitute. And although it is wonderful how easily this can be done in Ruby, I believe that the stream paradigm itself merits to be looked at<br />at a more abstract level.<br />This said, my implementation gives you the best of the two worlds of course. The <i>functional</i> layer being only a wrapper around the Ruby Object layer you can use both at will.<br /><br /><hr></hr><br /><br /><a name="diving_into_streams" href="#toc">Back to TOC</a><br /><span class="headline1">Diving into Streams</span><br /><br />Let us have some fun now and play with streams:<br /><pre class="myblog"><font color="#a52a2a">1 </font><font color="#a020f0">include</font> <font color="#2e8b57"><b>Lab419</b></font>::<font color="#2e8b57"><b>SimpleStream</b></font><br /><font color="#a52a2a">2 </font>constant = cons_stream( <span style="background-color: #f2f2f2"><font color="#ff00ff">42</font></span> ){ constant }<br /><font color="#a52a2a">3 </font>p constant.head <font color="#0000ff"># --> 42</font><br /><font color="#a52a2a">4 </font>p constant.tail.head <font color="#0000ff"># --> 42</font><br /><font color="#a52a2a">5 </font>p constant.tail <font color="#0000ff"># --> #<Lab419::SimpleStream::Stream:0xb7d68424<br /> # @tail=#<Proc:0xb7d6867c@diving-into-streams-01.rb:2>, @head=42></font><br /></pre><br />It is quite remarkable, that we can use <code>constant</code> inside the <i>promise</i> ( which is implemented as the code block, remember? ) without any problems [line #2]. No surprises in lines 3 & 4. And if we remember the second invariant line #5 is not going to surprise us either. Furthermore we can already see some of the internal representation of streams. As mentioned before it is a plain vanilla Ruby class, that exposes an Object Oriented interface to streams as well.<br /><br />The aforementioned <i>recursiveness</i> of streams can be seen in the next example again.<br /><pre class="myblog"><font color="#a52a2a">1 </font><font color="#a020f0">include</font> <font color="#2e8b57"><b>Lab419</b></font>::<font color="#2e8b57"><b>SimpleStream</b></font><br /><font color="#a52a2a">2 </font><font color="#a020f0">def</font> <font color="#008b8b">integers</font> from=<span style="background-color: #f2f2f2"><font color="#ff00ff">0</font></span><br /><font color="#a52a2a">3 </font> cons_stream( from ){ integers( from + <span style="background-color: #f2f2f2"><font color="#ff00ff">1</font></span> ) }<br /><font color="#a52a2a">4 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">5 </font><br /><font color="#a52a2a">6 </font>i = integers<br /><font color="#a52a2a">7 </font>p i.head <font color="#0000ff"># --> 0</font><br /><font color="#a52a2a">8 </font>p i.tail.tail.head <font color="#0000ff"># --> 2</font><br /></pre><br />As soon as we have understood the lazy nature of the promise the <i>recursive</i> call to <code>integers</code> does not trouble us any more. You can compute the following without any problems:<br /><pre class="myblog"><font color="#a52a2a">1 </font><span style="background-color: #f2f2f2"><font color="#ff00ff">42_420</font></span>.times <font color="#a52a2a"><b>do</b></font><br /><font color="#a52a2a">2 </font> i = i.tail<br /><font color="#a52a2a">3 </font><font color="#a52a2a"><b>end</b></font><br /><font color="#a52a2a">4 </font>p i.head<br /></pre><br /><br />One of the big advantages of using streams is that we can exploit these <i>cheap</i> recursions of streams to formulate elegant recursive procedures without the danger of stack overflows and even those who could not be tail call optimized.<br /><br />As an example let us consider the Fibonacci numbers as a stream. Then a natural definition of such a stream, let us call it <i>fibo</i> would be:<br /><br /><pre><br /> <i>fibo</i> is a stream for which the head is 0.<br /> <i>fibo</i> is also a stream for which the head of its tail is 1.<br /> <i>fibo</i> is eventually a stream for which the head of the tail of the tail is the<br /> sum of the head and the head of the tail.<br /></pre><br /><br />We will try now to translate this into Ruby, and we will see that this can be done in an analogous way, without any effort:<br /><pre class="myblog"><font color="#a52a2a"> 1 </font><font color="#a020f0">include</font> <font color="#2e8b57"><b>Lab419</b></font>::<font color="#2e8b57"><b>SimpleStream</b></font><br /><font color="#a52a2a"> 2 </font><br /><font color="#a52a2a"> 3 </font><font color="#a020f0">def</font> <font color="#008b8b">add_streams</font> lhs, rhs<br /><font color="#a52a2a"> 4 </font> cons_stream( lhs.head + rhs.head ){<br /><font color="#a52a2a"> 5 </font> add_streams( lhs.tail, rhs.tail )<br /><font color="#a52a2a"> 6 </font> }<br /><font color="#a52a2a"> 7 </font><font color="#a020f0">end</font><br /><font color="#a52a2a"> 8 </font><br /><font color="#a52a2a"> 9 </font><font color="#a020f0">def</font> <font color="#008b8b">fibo</font><br /><font color="#a52a2a">10 </font> cons_stream( <span style="background-color: #f2f2f2"><font color="#ff00ff">0</font></span> ) {<br /><font color="#a52a2a">11 </font> cons_stream( <span style="background-color: #f2f2f2"><font color="#ff00ff">1</font></span> ){<br /><font color="#a52a2a">12 </font> add_streams( fibo, fibo.tail )<br /><font color="#a52a2a">13 </font> }<br /><font color="#a52a2a">14 </font> }<br /><font color="#a52a2a">15 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">16 </font><br /><font color="#a52a2a">17 </font>p fibo[<span style="background-color: #f2f2f2"><font color="#ff00ff">0</font></span>..<span style="background-color: #f2f2f2"><font color="#ff00ff">6</font></span>]<br /></pre><br />Running this program delivers<br /><pre class="myblog bash"><br />512/31 > ruby -rlab419/functional/streams diving-into-streams-03.rb <br />[0, 1, 1, 2, 3, 5, 8]<br /></pre><br /><br />Please note, that this implementation of <code>add_streams</code> does not consider the possibility of finite streams, we will come to that later.<br />Another important point is that we can consider streams as <code>Enumerables</code> very easily too.<br /><br />Actually this is about all, yes the power of streams has much to do with their simplicity. Everything else I will talk about now will not introduce anything new. It will however show some of the conveniences built into the implementation, and show us how to handle <i>Finite Streams</i>. It is somehow funny that <b>they are the special case</b> while <b>infinite streams are the normal case</b>.<br /><br /><hr></hr><br /><br /><a name="ruby_meets_streams" href="#toc">Back to TOC</a><br /><span class="headline1">Ruby Meets Streams</span><br /><br /><b>Finite Streams</b><br />Finite Streams are just a special case of Infinite Streams. A mathematical definition of finite streams might look like the following:<br /><pre><br /> A finite is a stream for which exists a promise delivering an <i>empty</i> stream.<br /> An empty stream is defined as a stream for which the tail delivers an empty stream<br /> and for which the behavior of head remains undefined.<br /></pre><br />Following this definition and having already an <code>EmptyStream</code> at our disposal it is straightforward to define a finite stream from a given number of objects:<br /><pre class="myblog"><font color="#a52a2a">1 </font><font color="#a020f0">def</font> <font color="#008b8b">finite_stream</font> *elements<br /><font color="#a52a2a">2 </font> <font color="#a52a2a"><b>return</b></font> <font color="#2e8b57"><b>EmptyStream</b></font> <font color="#a52a2a"><b>if</b></font> elements.empty?<br /><font color="#a52a2a">3 </font> cons_stream( elements.shift ) { finite_stream( *elements ) }<br /><font color="#a52a2a">4 </font><font color="#a020f0">end</font><br /></pre><br /><br />But as infinity is such an important concept for streams, we need a way to make finite streams infinite, the most logical approach is to cycle through them. Such a task is of course almost frivolously easy:<br /><pre class="myblog"><font color="#a52a2a">1 </font><font color="#a020f0">include</font> <font color="#2e8b57"><b>Lab419</b></font>::<font color="#2e8b57"><b>SimpleStream</b></font><br /><font color="#a52a2a">2 </font><br /><font color="#a52a2a">3 </font><font color="#a020f0">def</font> <font color="#008b8b">cyclic_stream</font> elements, current=<span style="background-color: #f2f2f2"><font color="#ff00ff">0</font></span><br /><font color="#a52a2a">4 </font> cons_stream( elements[current] ) { cyclic_stream( elements, current.succ % elements.size ) }<br /><font color="#a52a2a">5 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">6 </font><br /><font color="#a52a2a">7 </font>primes = cyclic_stream [ <span style="background-color: #f2f2f2"><font color="#ff00ff">2</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">3</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">5</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">7</font></span> ]<br /><font color="#a52a2a">8 </font><br /><font color="#a52a2a">9 </font>p primes[<span style="background-color: #f2f2f2"><font color="#ff00ff">0</font></span>..<span style="background-color: #f2f2f2"><font color="#ff00ff">9</font></span>]<br /></pre><br /> <br /><b>Ruby Objects as Streams</b><br />The implementation of streams in Labrador (branch <a href="http://rubyforge.org/frs/?group_id=3824">lab419-functional-0.1</a> ) is quite straightforward and provides a functional interface as implemented in module <code>Lab419::SimpleStream</code> or an object oriented interface.<br />Please note that you can use all enumerables as streams by means of the <code>#to_stream</code> method, while <code>String</code> implements a <code>#to_byte_stream</code> method too.<br /><br /><hr></hr><br /><br /><a name="streams_applications" href="#toc">Back to TOC</a><br /><span class="headline1">Streams' Applications</span><br /><br />Without this section of this Blog entry one might be thinking that streams are a neat concept but that would be about all.<br /><br />Abelson shows a very elegant solution of the N Queens problem by using streams. I will settle here for something less spectacular.<br /><br /><b>Streams as a Lexer</b><br />The name <i>stream</i> somehow induces a typical usage of streams. <b>IOStreams</b> can very<br />well be implemented as streams. The following example implements a very simple lexer that works as a stream.<br /><br /><pre class="myblog"><font color="#a52a2a"> 1 </font><br /><font color="#a52a2a"> 2 </font><font color="#a020f0">class</font> <font color="#2e8b57"><b>String</b></font><br /><font color="#a52a2a"> 3 </font> <font color="#a020f0">def</font> <font color="#008b8b">next_token</font> *tokens<br /><font color="#a52a2a"> 4 </font> tokens.each <font color="#a52a2a"><b>do</b></font> | <font color="#008b8b">token</font> |<br /><font color="#a52a2a"> 5 </font> <font color="#a52a2a"><b>if</b></font> <span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">^</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>token<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span> === <span style="background-color: #f2f2f2"><font color="#ff00ff">self</font></span> <font color="#a52a2a"><b>then</b></font><br /><font color="#a52a2a"> 6 </font> <font color="#a52a2a"><b>return</b></font> [ <font color="#008b8b">$1</font> || <font color="#008b8b">$&</font> , <span style="background-color: #f2f2f2"><font color="#ff00ff">self</font></span>.sub( <span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">^</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>token<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">""</font></span> ) ]<br /><font color="#a52a2a"> 7 </font> <font color="#a52a2a"><b>end</b></font><br /><font color="#a52a2a"> 8 </font> <font color="#a52a2a"><b>end</b></font><br /><font color="#a52a2a"> 9 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a">10 </font> [ <span style="background-color: #f2f2f2"><font color="#ff00ff">nil</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">nil</font></span> ]<br /><font color="#a52a2a">11 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">12 </font><br /><font color="#a52a2a">13 </font><font color="#a020f0">def</font> <font color="#008b8b">lexer_stream</font> io, line=<span style="background-color: #f2f2f2"><font color="#6a5acd">""</font></span>, *tokens<br /><font color="#a52a2a">14 </font> line = io.readline.chomp <font color="#a52a2a"><b>if</b></font> line.empty?<br /><font color="#a52a2a">15 </font> token, line = line.next_token( *tokens )<br /><font color="#a52a2a">16 </font> cons_stream( token ){ lexer_stream( io, line, *tokens ) }<br /><font color="#a52a2a">17 </font><font color="#a52a2a"><b>rescue</b></font> <font color="#2e8b57"><b>EOFError</b></font><br /><font color="#a52a2a">18 </font> <font color="#2e8b57"><b>EmptyStream</b></font><br /><font color="#a52a2a">19 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">20 </font><br /><font color="#a52a2a">21 </font>lexer = lexer_stream( <font color="#008b8b">$stdin</font>, <span style="background-color: #f2f2f2"><font color="#6a5acd">""</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">\s</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">*(\w+)</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">\s</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">*(\d+)</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">\s</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">*(.)</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span> )<br /><font color="#a52a2a">22 </font><br /><font color="#a52a2a">23 </font><font color="#a52a2a"><b>until</b></font> lexer.empty? <font color="#a52a2a"><b>do</b></font><br /><font color="#a52a2a">24 </font> puts lexer.head<br /><font color="#a52a2a">25 </font> lexer = lexer.tail<br /><font color="#a52a2a">26 </font><font color="#a52a2a"><b>end</b></font><br /><font color="#a52a2a">27 </font><br /></pre><br /><br />First we define a <code>get_token</code> helper in class <code>String</code>. This does not modifie its receiver but returns the parsed string and the rest of the line. It is really just a toy to test the stream part of the example.<br /><br />Things become a little bit more interesting at line #13. A <code>lexer_stream</code> is constructed by getting the first token from the input (<code>io</code>) which is line buffered by means of <code>line</code> and defining the tail as a lexer_stream with the same io, the same tokens and the new line buffer. Simple?<br /><br />And it works like charm. <br /><br />In parsing it is sometimes convenient to implement a certain lookahead. Imagine how elegantly a parser can be written if it has the possibility to push tokens back on the input stream. With a stream based lexer this is a one-liner.<br /><br /><code>lexer = cons_stream( pushbacktoken ){ lexer } </code><br /><br />So we will have <br /><pre class="myblog"><font color="#a52a2a"> 1 </font><br /><font color="#a52a2a"> 2 </font><font color="#a020f0">class</font> <font color="#2e8b57"><b>String</b></font><br /><font color="#a52a2a"> 3 </font> <font color="#a020f0">def</font> <font color="#008b8b">next_token</font> *tokens<br /><font color="#a52a2a"> 4 </font> tokens.each <font color="#a52a2a"><b>do</b></font> | <font color="#008b8b">token</font> |<br /><font color="#a52a2a"> 5 </font> <font color="#a52a2a"><b>if</b></font> <span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">^</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>token<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span> === <span style="background-color: #f2f2f2"><font color="#ff00ff">self</font></span> <font color="#a52a2a"><b>then</b></font><br /><font color="#a52a2a"> 6 </font> <font color="#a52a2a"><b>return</b></font> [ <font color="#008b8b">$1</font> || <font color="#008b8b">$&</font> , <span style="background-color: #f2f2f2"><font color="#ff00ff">self</font></span>.sub( <span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">^</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>token<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">""</font></span> ) ]<br /><font color="#a52a2a"> 7 </font> <font color="#a52a2a"><b>end</b></font><br /><font color="#a52a2a"> 8 </font> <font color="#a52a2a"><b>end</b></font><br /><font color="#a52a2a"> 9 </font> <font color="#a020f0">end</font><br /><font color="#a52a2a">10 </font> [ <span style="background-color: #f2f2f2"><font color="#ff00ff">nil</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">nil</font></span> ]<br /><font color="#a52a2a">11 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">12 </font><br /><font color="#a52a2a">13 </font><font color="#a020f0">def</font> <font color="#008b8b">lexer_stream</font> io, line=<span style="background-color: #f2f2f2"><font color="#6a5acd">""</font></span>, *tokens<br /><font color="#a52a2a">14 </font> line = io.readline.chomp <font color="#a52a2a"><b>if</b></font> line.empty?<br /><font color="#a52a2a">15 </font> token, line = line.next_token( *tokens )<br /><font color="#a52a2a">16 </font> cons_stream( token ){ lexer_stream( io, line, *tokens ) }<br /><font color="#a52a2a">17 </font><font color="#a52a2a"><b>rescue</b></font> <font color="#2e8b57"><b>EOFError</b></font><br /><font color="#a52a2a">18 </font> <font color="#2e8b57"><b>EmptyStream</b></font><br /><font color="#a52a2a">19 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">20 </font><br /><font color="#a52a2a">21 </font><font color="#a020f0">def</font> <font color="#008b8b">push_back</font> a_stream, a_token<br /><font color="#a52a2a">22 </font> cons_stream( a_token ){ a_stream }<br /><font color="#a52a2a">23 </font><font color="#a020f0">end</font><br /><font color="#a52a2a">24 </font><br /><font color="#a52a2a">25 </font>lexer = lexer_stream( <font color="#008b8b">$stdin</font>, <span style="background-color: #f2f2f2"><font color="#6a5acd">""</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">\s</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">*(\w+)</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">\s</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">*(\d+)</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span>, <span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">\s</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">*(.)</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">/</font></span> )<br /><font color="#a52a2a">26 </font><br /><font color="#a52a2a">27 </font><font color="#a52a2a"><b>until</b></font> lexer.empty? <font color="#a52a2a"><b>do</b></font><br /><font color="#a52a2a">28 </font> <font color="#a52a2a"><b>if</b></font> lexer.head == <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">break</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span> <font color="#a52a2a"><b>then</b></font><br /><font color="#a52a2a">29 </font> lexer = push_back( <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">break</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span>, lexer )<br /><font color="#a52a2a">30 </font> puts <span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">returning </font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">#{</font></span>lexer.inspect<span style="background-color: #f2f2f2"><font color="#6a5acd">}</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">"</font></span><br /><font color="#a52a2a">31 </font> <font color="#a52a2a"><b>break</b></font><br /><font color="#a52a2a">32 </font> <font color="#a52a2a"><b>end</b></font><br /><font color="#a52a2a">33 </font><br /><font color="#a52a2a">34 </font> puts lexer.head<br /><font color="#a52a2a">35 </font> lexer = lexer.tail<br /><font color="#a52a2a">36 </font><font color="#a52a2a"><b>end</b></font><br /><font color="#a52a2a">37 </font><br /></pre><br /><br /><br /><br />That is about all, please check out the project at Rubyforge and thanx for your attention.<br /><br /><hr><br /><b>Update</b> <br />thanks to Brian Candler<br /><br />Brian pointed out correctly that Ruby Enumerators are lazy too in Ruby1.9, and <i>almost lazy</i> in 1.8. He made a nice contribution to Facets where he made them lazy in Ruby1.8 too.<br />Please check it out <a href="http://facets.rubyforge.org/git?p=facets.git;a=blob_plain;f=lib/lore/facets/enumerator.rb;hb=HEAD">here on Rubyforge.</a><br /><br />I knew actually that Enumerables and Enumerators could be lazy, but I ignored that Ruby1.9 had a new syntax which really encourages this kind of constructs.<br /><br /><pre class="myblog"><font color="#a52a2a">1 </font>x = <font color="#2e8b57"><b>Enumerator</b></font>::new <font color="#a52a2a"><b>do</b></font> | <font color="#008b8b">enator</font> | <br /><font color="#a52a2a">2 </font> <font color="#a52a2a"><b>loop</b></font> <font color="#a52a2a"><b>do</b></font><br /><font color="#a52a2a">3 </font> enator.yield rand <br /><font color="#a52a2a">4 </font> <font color="#a52a2a"><b>end</b></font><br /><font color="#a52a2a">5 </font><font color="#a52a2a"><b>end</b></font><br /><font color="#a52a2a">6 </font><br /><font color="#a52a2a">7 </font>p x.next<br /><font color="#a52a2a">8 </font>p x.next<br /><font color="#a52a2a">9 </font>p x.take( <span style="background-color: #f2f2f2"><font color="#ff00ff">10</font></span> )<br /></pre><br /><br />As a matter of fact Generators, Fibers and Enumerators in 1.9 are very, very promising. <br /></div>Robert Doberhttp://www.blogger.com/profile/06304126315464640783noreply@blogger.com2tag:blogger.com,1999:blog-6539205209092092742.post-13862136835085388462007-09-30T19:15:00.000+02:002007-09-30T23:21:09.081+02:00Mixin and State, Any Problems?<!-- vim: sw=2 sts=2 expandtab nu tw=0:<br /> --><br /> Last week we were having a discussion about Ruby with a relative newbie who was wondering<br /> about how Mixins and State work together.<br /> I was sure that the discussion would be brief, explaining that all mixed in methods have full<br /> access to its receivers state and that of course a Mixin could define <code>initialize</code> too.<br /><br /> <span style="font-weight: bold;"><span style="font-size:130%;">And What About Class State?</span></span><br /> ... he said. Well, er, that is only a minor complication, I said. What exactly would you like to achieve?<br /><br /> We came up with a very simple, but useful, example. An Instance Tracker. It should allow us to easily<br /> collect all instances of a class. The collected instances should than be accessible by a class method.<br /><br /> We decided to have some fun with TDD and specified the Tracker as follows:<br /><pre><!-- <span class="ident">require</span> <span class="punct">'</span><span class="string">test/unit</span><span class="punct">'</span><br /> <span class="ident">require</span> <span class="punct">'</span><span class="string">tracker1</span><span class="punct">'</span><br />--> <span class="keyword">class </span><span class="class">TestInstanceTracker</span> <span class="punct"><</span> <span class="constant">Test</span><span class="punct">::</span><span class="constant">Unit</span><span class="punct">::</span><span class="constant">TestCase</span><br /> <span class="keyword">def </span><span class="method">test_01_basic</span><br /> <span class="ident">a</span> <span class="punct">=</span> <span class="constant">Class</span><span class="punct">::</span><span class="ident">new</span> <span class="punct">{</span><br /> <span class="ident">include</span> <span class="constant">Tracker</span><br /> <span class="punct">}</span><br /> <span class="ident">a1</span> <span class="punct">=</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">new</span><br /> <span class="ident">assert_equal</span> <span class="punct">[</span><span class="ident">a1</span><span class="punct">],</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">instances</span><br /> <span class="ident">a2</span> <span class="punct">=</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">new</span><br /> <span class="ident">assert_equal</span> <span class="punct">[</span><span class="ident">a1</span><span class="punct">,</span> <span class="ident">a2</span><span class="punct">],</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">instances</span><br /> <span class="keyword">end</span> <span class="comment"># def test_01_basic</span><br /> <span class="keyword">end</span> <span class="comment"># class TestInstanceTracker < Test::Unit::TestCase</span><br /></pre><br /><br /> Ok it did not take me a long time to figure it out, at first one had to assure that any new instance were added<br /> to the instance collection, thus <br /><pre> <span class="keyword">module </span><span class="module">Tracker</span><br /> <span class="keyword">def </span><span class="method">initialize</span><br /> <span class="constant">self</span><span class="punct">.</span><span class="ident">class</span><span class="punct">.</span><span class="ident">instances</span> <span class="punct"><<</span> <span class="constant">self</span><br /> <span class="keyword">end</span><br /></pre><br /> and than I had to labor that <code>self.class.instances</code> worked, therefore...<br /><pre> <span class="keyword">def </span><span class="method">self.included</span> <span class="ident">into_a_class</span><br /> <span class="ident">into_a_class</span><span class="punct">.</span><span class="ident">instance_variable_set</span> <span class="punct">"</span><span class="string">@instances</span><span class="punct">",</span> <span class="punct">[]</span><br /> <span class="keyword">class </span><span class="punct"><<</span> <span class="ident">into_a_class</span><span class="punct">;</span> <span class="ident">attr_reader</span> <span class="symbol">:instances</span> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /></pre><br /> Needless to say that code passed our testsuite (that was two assertions) brilliantly.<br /> Now even if I had not read Kent Beck's <a href="http://www.amazon.com/Extreme-Programming-Explained-Embrace-Change/dp/0201616416">eXtreme Programming</a>,I had known that the testsuite was er incomplete.<br /><br /> As a matter of fact my collegue came up with this in no time<br /><pre> <span class="keyword">def </span><span class="method">test_02_subclass</span><br /> <span class="ident">a</span> <span class="punct">=</span> <span class="constant">Class</span><span class="punct">::</span><span class="ident">new</span> <span class="punct">{</span><br /> <span class="ident">include</span> <span class="constant">Tracker</span><br /> <span class="punct">}</span><br /> <span class="ident">b</span> <span class="punct">=</span> <span class="constant">Class</span><span class="punct">::</span><span class="ident">new</span> <span class="ident">a</span><br /> <span class="ident">a1</span> <span class="punct">=</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">new</span><br /> <span class="ident">assert_equal</span> <span class="punct">[</span><span class="ident">a1</span><span class="punct">],</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">instances</span><br /> <span class="ident">b1</span> <span class="punct">=</span> <span class="ident">b</span><span class="punct">.</span><span class="ident">new</span><br /> <span class="ident">assert_equal</span> <span class="punct">[</span><span class="ident">a1</span><span class="punct">],</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">instances</span><br /> <span class="ident">assert_equal</span> <span class="punct">[</span><span class="ident">b1</span><span class="punct">],</span> <span class="ident">b</span><span class="punct">.</span><span class="ident">instances</span><br /> <span class="ident">a2</span> <span class="punct">=</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">new</span><br /> <span class="ident">assert_equal</span> <span class="punct">[</span><span class="ident">a1</span><span class="punct">,</span> <span class="ident">a2</span><span class="punct">],</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">instances</span><br /> <span class="ident">assert_equal</span> <span class="punct">[</span><span class="ident">b1</span><span class="punct">],</span> <span class="ident">b</span><span class="punct">.</span><span class="ident">instances</span><br /> <span class="keyword">end</span> <span class="comment"># def test_02_subclass</span><br /></pre><br /> Yielding this nice little result<br /> <pre><br /> 1) Error:<br />test_02_subclass(TestInstanceTracker):<br />NoMethodError: undefined method `<<' for nil:NilClass<br /> ./tracker2.rb:4:in `initialize'<br /> test2.rb:24:in `new'<br /> test2.rb:24:in `test_02_subclass'<br /></pre><br /> <span style="font-weight: bold;"><span style="font-size:130%;">And Subclasses?</span></span><br /><br /> Ok so he wants subclasses, yes sure, so would I, let us see...<br /> As we are already telling the class we are included into to do some stuff, why not just tell it to include us<br /> into any subclass which is created from itself<br /><br /> Such things can be done with the help of the class callback <code>inherited</code>.<br /><pre> <span class="keyword">def </span><span class="method">self.included</span> <span class="ident">into_a_class</span><br /> <span class="ident">into_a_class</span><span class="punct">.</span><span class="ident">instance_variable_set</span> <span class="punct">"</span><span class="string">@instances</span><span class="punct">",</span> <span class="punct">[]</span><br /> <span class="keyword">class </span><span class="punct"><<</span> <span class="ident">into_a_class</span> <br /> <span class="ident">attr_reader</span> <span class="symbol">:instances</span> <br /> <span class="keyword">def </span><span class="method">inherited</span> <span class="ident">by_class</span><br /> <span class="ident">by_class</span><span class="punct">.</span><span class="ident">send</span> <span class="symbol">:include</span><span class="punct">,</span> <span class="constant">Tracker</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span> <span class="comment"># def self.included into_a_class</span><br /></pre><br /> This again made the tests pass, which means that we had to come up with new tests.<br /><br /> <span style="font-weight: bold;"><span style="font-size:130%;">And Module Inclusion?</span></span><br /> <br /> He cannot be serious, what does he want?<br /> Ah yes well, the following<br /><pre> <span class="keyword">def </span><span class="method">test_03_includes</span><br /> <span class="ident">m</span> <span class="punct">=</span> <span class="constant">Module</span><span class="punct">::</span><span class="ident">new</span> <span class="punct">{</span><br /> <span class="ident">include</span> <span class="constant">Tracker</span><br /> <span class="punct">}</span><br /> <span class="ident">a</span> <span class="punct">=</span> <span class="constant">Class</span><span class="punct">::</span><span class="ident">new</span> <span class="punct">{</span><br /> <span class="ident">include</span> <span class="ident">m</span><br /> <span class="punct">}</span><br /> <span class="ident">b</span> <span class="punct">=</span> <span class="constant">Class</span><span class="punct">::</span><span class="ident">new</span> <span class="ident">a</span><br /> <span class="ident">a1</span> <span class="punct">=</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">new</span><br /> <span class="ident">assert_equal</span> <span class="punct">[</span><span class="ident">a1</span><span class="punct">],</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">instances</span><br /> <span class="ident">b1</span> <span class="punct">=</span> <span class="ident">b</span><span class="punct">.</span><span class="ident">new</span><br /> <span class="ident">assert_equal</span> <span class="punct">[</span><span class="ident">a1</span><span class="punct">],</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">instances</span><br /> <span class="ident">assert_equal</span> <span class="punct">[</span><span class="ident">b1</span><span class="punct">],</span> <span class="ident">b</span><span class="punct">.</span><span class="ident">instances</span><br /> <span class="ident">a2</span> <span class="punct">=</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">new</span><br /> <span class="ident">assert_equal</span> <span class="punct">[</span><span class="ident">a1</span><span class="punct">,</span> <span class="ident">a2</span><span class="punct">],</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">instances</span><br /> <span class="ident">assert_equal</span> <span class="punct">[</span><span class="ident">b1</span><span class="punct">],</span> <span class="ident">b</span><span class="punct">.</span><span class="ident">instances</span><br /> <span class="keyword">end</span> <span class="comment"># def test_03_includes</span><br /></pre><br /> Actually it is pretty simple, when we are included into a class we do what we did so far:<br /> <ul><br /> <li>Define the class instance variable</li><br /> <li>Define the class level accessor to the class instance variable</li><br /> <li>Tell the class to include us into all its subclasses via the <code>self.inherited</self> callback</li><br /> </ul><br /> When we are included into a module however, we have to do all the above again, that is in<br /><pre> <span class="keyword">def </span><span class="method">self.included</span> <span class="ident">into_module</span><br /> <span class="keyword">case</span> <span class="ident">into_module</span><br /> <span class="keyword">when</span> <span class="constant">Module</span><br /> <span class="comment">#(1)</span><br /></pre><br /> we have to put the exact code of the method at #(1) again. <br /> Infinite code has some serious drawbacks though, very long development time being one that springs into mind first.<br /><br /> Ruby however let us define such a strange recursion without any problem:<br /><pre> <span class="keyword">module </span><span class="module">Tracker</span><br /> <span class="constant">SelfIncludedProc</span> <span class="punct">=</span> <span class="ident">proc</span> <span class="punct">{</span><br /> <span class="punct">|</span> <span class="ident">into_a_module</span> <span class="punct">|</span><br /> <span class="keyword">case</span> <span class="ident">into_a_module</span><br /> <span class="keyword">when</span> <span class="constant">Class</span><br /> <span class="ident">into_a_module</span><span class="punct">.</span><span class="ident">instance_variable_set</span> <span class="punct">"</span><span class="string">@instances</span><span class="punct">",</span> <span class="punct">[]</span><br /> <span class="keyword">class </span><span class="punct"><<</span> <span class="ident">into_a_module</span> <br /> <span class="ident">attr_reader</span> <span class="symbol">:instances</span> <br /> <span class="keyword">def </span><span class="method">inherited</span> <span class="ident">by_class</span><br /> <span class="ident">by_class</span><span class="punct">.</span><span class="ident">send</span> <span class="symbol">:include</span><span class="punct">,</span> <span class="constant">Tracker</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /> <span class="keyword">when</span> <span class="constant">Module</span><br /> <span class="keyword">class </span><span class="punct"><<</span> <span class="ident">into_a_module</span><span class="punct">;</span> <span class="constant">self</span> <span class="keyword">end</span><span class="punct">.</span><span class="ident">send</span> <span class="symbol">:define_method</span><span class="punct">,</span> <span class="symbol">:included</span><span class="punct">,</span> <span class="punct">&</span><span class="constant">SelfIncludedProc</span> <br /> <span class="keyword">end</span> <span class="comment"># case into_a_module</span><br /> <span class="punct">}</span><br /> <br /> <span class="keyword">class </span><span class="punct"><<</span> <span class="constant">self</span><span class="punct">;</span> <span class="constant">self</span> <span class="keyword">end</span><span class="punct">.</span><span class="ident">send</span> <span class="symbol">:define_method</span><span class="punct">,</span> <span class="symbol">:included</span><span class="punct">,</span> <span class="punct">&</span><span class="constant">SelfIncludedProc</span> <br /><br /> <span class="keyword">def </span><span class="method">initialize</span><br /> <span class="constant">self</span><span class="punct">.</span><span class="ident">class</span><span class="punct">.</span><span class="ident">instances</span> <span class="punct"><<</span> <span class="constant">self</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /></pre><br /> And that passes our tests again.<br /><br /> But there is one important test that does not pass:<br /><br /> <b>This code just does not seem right</b><br /><br /> I thought about it a little bit and than the scales fell from my eyes, of course it is not right, I was doing two<br /> things at the same place, coding and metacoding, so all I need to do is to tear these two parts apart.<br /><br /> The first and relatively uninteresting part is the application code that creates a class instance variable, its accessor and the automatic addition of new instances.<br /><br /> The second part is simply a kind of a <code><b>Module</b></code> that allows us to define class level code for<br /> all subclasses and even via indirect inclusion. <br /> <p>That's what I want, I just have no idea how to call such a beast, so I called it <code><b>RecModule</b></code>,<br /> great. I will accept all naming suggestions very cheerfully.<br /> </p><br /><br /> <span style="font-weight: bold;"><span style="font-size:130%;">A Home Made Module Class</span></span><br /><br /> <p>Let us have a look at the code:</p><br /><pre><span class="keyword">class </span><span class="class">RecModule</span> <span class="punct"><</span> <span class="constant">Module</span><br /> <span class="keyword">def </span><span class="method">recursive_included</span> <span class="punct">&</span><span class="ident">blk</span><br /> <span class="comment"># Define a block that defines the included</span><br /> <span class="ident">inclusion</span> <span class="punct">=</span> <span class="ident">proc</span> <span class="punct">{</span> <span class="punct">|</span> <span class="ident">by_module</span> <span class="punct">|</span><br /> <span class="keyword">super</span> <span class="ident">by_module</span><br /> <span class="ident">blk</span><span class="punct">.</span><span class="ident">call</span> <span class="ident">by_module</span> <span class="keyword">if</span> <span class="constant">Class</span> <span class="punct">===</span> <span class="ident">by_module</span><br /> <span class="keyword">class </span><span class="punct"><<</span> <span class="ident">by_module</span><span class="punct">;</span> <span class="constant">self</span> <span class="keyword">end</span><span class="punct">.</span><br /> <span class="ident">send</span> <span class="symbol">:define_method</span><span class="punct">,</span> <span class="symbol">:included</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">inclusion</span><br /> <span class="punct">}</span><br /> <span class="comment"># Define included in base module</span><br /> <span class="keyword">class </span><span class="punct"><<</span> <span class="constant">self</span><span class="punct">;</span> <span class="constant">self</span> <span class="keyword">end</span><span class="punct">.</span><br /> <span class="ident">send</span> <span class="symbol">:define_method</span><span class="punct">,</span> <span class="symbol">:included</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">inclusion</span><br /> <span class="keyword">end</span><br /><span class="keyword">end</span> <span class="comment"># class RecModule</span><br /></pre><br /> <p>Yep and?<br /> </p><br /><p>Ok it will make more sense to you if we use it... but please note the repeating pattern of the way to define <code><b>Module#included</b></code> by means of a self referring block into our including modules.<br /></p><br /><pre><span class="constant">M1</span> <span class="punct">=</span> <span class="constant">RecModule</span><span class="punct">.</span><span class="ident">new</span> <span class="punct">{</span><br /> <span class="ident">recursive_included</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">by_class</span><span class="punct">|</span><br /> <span class="keyword">class </span><span class="punct"><<</span> <span class="ident">by_class</span><br /> <span class="ident">attr_accessor</span> <span class="symbol">:x</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /><span class="punct">}</span><br /><br /><span class="constant">M2</span> <span class="punct">=</span> <span class="constant">RecModule</span><span class="punct">.</span><span class="ident">new</span> <span class="punct">{</span><br /> <span class="ident">include</span> <span class="constant">M1</span><br /><span class="punct">}</span><br /><span class="keyword">class </span><span class="class">A</span><br /> <span class="ident">include</span> <span class="constant">M2</span><br /><span class="keyword">end</span><br /><span class="constant">B</span> <span class="punct">=</span> <span class="constant">Class</span><span class="punct">::</span><span class="ident">new</span> <span class="constant">A</span><br /><br /><span class="constant">A</span><span class="punct">.</span><span class="ident">x</span> <span class="punct">=</span> <span class="number">46</span><br /><span class="constant">B</span><span class="punct">.</span><span class="ident">x</span> <span class="punct">=</span> <span class="number">42</span><br /><span class="ident">p</span> <span class="constant">A</span><span class="punct">.</span><span class="ident">x</span> <span class="comment"># --> 46</span><br /><span class="ident">p</span> <span class="constant">B</span><span class="punct">.</span><span class="ident">x</span> <span class="comment"># --> 42</span><br /></pre><br /><p>I have implemented a <code><b>recursive_include</b></code> that will actually only execute its block for including<br />classes, but other similar tricks could have been used.<br /></p><br /><h1>And Does It Really Work?</h1><br /><p>Best way to find out is to try it, I will just reimplement the <i>InstanceTracker</i> with the new idioms...<br /></p><br /><br /><pre><span class="constant">InstanceTracker</span> <span class="punct">=</span> <span class="constant">RecModule</span><span class="punct">.</span><span class="ident">new</span> <span class="punct">{</span><br /> <span class="ident">recursive_included</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">by_class</span><span class="punct">|</span><br /> <span class="keyword">class </span><span class="punct"><<</span> <span class="ident">by_class</span><br /> <span class="keyword">def </span><span class="method">instances</span><br /> <span class="attribute">@__instances__</span> <span class="punct">||=</span> <span class="punct">[]</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /><br /> <span class="keyword">def </span><span class="method">initialize</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">blk</span><br /> <span class="keyword">super</span><br /> <span class="constant">self</span><span class="punct">.</span><span class="ident">class</span><span class="punct">.</span><span class="ident">instances</span> <span class="punct"><<</span> <span class="constant">self</span><br /> <span class="keyword">end</span><br /><span class="punct">}</span><br /><br /><span class="constant">M2</span> <span class="punct">=</span> <span class="constant">RecModule</span><span class="punct">.</span><span class="ident">new</span> <span class="punct">{</span><br /> <span class="ident">include</span> <span class="constant">InstanceTracker</span><br /><span class="punct">}</span><br /><span class="keyword">class </span><span class="class">A</span><br /> <span class="ident">include</span> <span class="constant">M2</span><br /><span class="keyword">end</span><br /><span class="constant">B</span> <span class="punct">=</span> <span class="constant">Class</span><span class="punct">::</span><span class="ident">new</span> <span class="constant">A</span><br /><br /><span class="constant">A</span><span class="punct">.</span><span class="ident">new</span><br /><span class="constant">B</span><span class="punct">.</span><span class="ident">new</span><br /><span class="constant">A</span><span class="punct">.</span><span class="ident">new</span><br /><span class="ident">p</span> <span class="constant">A</span><span class="punct">.</span><span class="ident">instances</span> <span class="comment"># --> [#<A:0x2b66028>, #<A:0x2b65fd8>]</span><br /><span class="ident">p</span> <span class="constant">B</span><span class="punct">.</span><span class="ident">instances</span> <span class="comment"># --> [#<B:0x2b66014>]</span><br /></pre><br /><p>Please note that M2 could even be a regular Module, I however think that using a RecModule reflects the intent of the code better, but that could be a matter of taste. For those who would prefer to use modules down the inclusion chain, it works perfectly too.<br /></p><br /><p>I am not sure that this is already the maximum I could have done in abstracting this functionality, but 14 lines of code for implementing such a basic <code><b>InstanceTracker</b></code> feels as about ok (10 lines would be better sigh).<br /></p><br />There is however one thing that makes me quite nervous, I feel proud and pleased with myself about it...<br />Hopefully there will be some gentle reader who will tell me why I should not...Robert Doberhttp://www.blogger.com/profile/06304126315464640783noreply@blogger.com3tag:blogger.com,1999:blog-6539205209092092742.post-26929727867078023612007-08-18T23:19:00.000+02:002007-08-27T17:51:37.383+02:00No More Instance Variables in My Prototypes.Last week we have seen a very simple but working implementation of the Prototype Paradigm in Pure Ruby. I have striven to implement it without orbiting too much around the classical Ruby Programming Paradigm using/ Classes and Modules. Using the exposed methods of the PPP API we can indeed forget about modules and classes at all, notwithstanding that they are doing the work behind the scenes of course.<br />By looking at our examples however, we see that we use instance variables for properties (or attributes) of our objects to remain in the OO jargon.<br />By defining attribute accessors this fact can be hidden but we can by no means avoid<span style="font-weight: bold;"> </span>that instance variables are used directly to access the properties we have defined.<br /><br />This week we will add a feature to the Prototype implementation that allows us to define properties without any usage of instance variables. We will define properties by means of closures, and we will allow a very simple way to define properties on a <i>Per Object</i> base as well as on a <i>Per Prototype</i> base.<br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">Properties</span></span><br /><br />Properties contain information attached to an object. In pure Ruby we use instance variables very often to represent properties. As a matter of fact we are talking about attributes and they are implemented with the well known <tt>attr_*</tt><br />definitions:<br /><!-- ruby2html inlined source from pure-ruby-attributes.rb --><br /><pre><span class="keyword">class </span><span class="class">Person</span><br /><span class="ident">attr_reader</span> <span class="symbol">:name</span><br /><span class="ident">attr_accessor</span> <span class="symbol">:age</span><br /><span class="keyword">def </span><span class="method">initialize</span> <span class="ident">name</span><span class="punct">,</span> <span class="ident">age</span><span class="punct">=</span><span class="constant">nil</span><br /> <span class="attribute">@name</span> <span class="punct">=</span> <span class="ident">name</span><br /> <span class="attribute">@age</span> <span class="punct">=</span> <span class="ident">age</span> <span class="keyword">if</span> <span class="ident">age</span><br /><span class="keyword">end</span><br /><span class="keyword">end</span><br /><br /><span class="ident">spencer</span> <span class="punct">=</span> <span class="constant">Person</span><span class="punct">.</span><span class="ident">new</span> <span class="punct">"</span><span class="string">Tracy</span><span class="punct">",</span> <span class="number">107</span><br /><span class="ident">hepburn</span> <span class="punct">=</span> <span class="constant">Person</span><span class="punct">.</span><span class="ident">new</span> <span class="punct">"</span><span class="string">Katharine</span><span class="punct">"</span></pre><br />Obviously <tt><b>name</b></tt> and <tt><b>age</b></tt> are properties attached to each object and not shared amongst all objects of the same class. If we want such a beast in pure Ruby we can use <i>Class Instance Variables</i><a href="http://www.blogger.com/post-edit.g?blogID=6539205209092092742&postID=2692972786707802361#note_1"><sup>1</sup></a>.<br />The classical example of such a <i>shared</i> property is to count instances of the class, and that is what the following code does:<br /><!-- ruby2html inlined source from pure-ruby-attributes-with-civ.rb --><br /><pre><span class="keyword">class </span><span class="class">Person</span><br /><span class="ident">attr_reader</span> <span class="symbol">:name</span><br /><span class="ident">attr_accessor</span> <span class="symbol">:age</span><br /><span class="attribute">@count</span> <span class="punct">=</span> <span class="number">0</span><br /><span class="keyword">class </span><span class="punct"><<</span> <span class="constant">self</span><br /> <span class="ident">attr_accessor</span> <span class="symbol">:count</span><br /><span class="keyword">end</span><br /><span class="keyword">def </span><span class="method">initialize</span> <span class="ident">name</span><span class="punct">,</span> <span class="ident">age</span><span class="punct">=</span><span class="constant">nil</span><br /> <span class="attribute">@name</span> <span class="punct">=</span> <span class="ident">name</span><br /> <span class="attribute">@age</span> <span class="punct">=</span> <span class="ident">age</span> <span class="keyword">if</span> <span class="ident">age</span><br /> <span class="constant">self</span><span class="punct">.</span><span class="ident">class</span><span class="punct">.</span><span class="ident">count</span> <span class="punct">+=</span> <span class="number">1</span><br /><span class="keyword">end</span><br /><span class="keyword">end</span><br /><br /><br /><span class="ident">spencer</span> <span class="punct">=</span> <span class="constant">Person</span><span class="punct">.</span><span class="ident">new</span> <span class="punct">"</span><span class="string">Tracy</span><span class="punct">",</span> <span class="number">107</span><br /><span class="ident">puts</span> <span class="constant">Person</span><span class="punct">.</span><span class="ident">count</span> <span class="comment"># -> 1</span><br /><span class="ident">hepburn</span> <span class="punct">=</span> <span class="constant">Person</span><span class="punct">.</span><span class="ident">new</span> <span class="punct">"</span><span class="string">Katharine</span><span class="punct">"</span><br /><span class="ident">puts</span> <span class="constant">Person</span><span class="punct">.</span><span class="ident">count</span> <span class="comment"># -> 2</span></pre><br /><hr /><br /><a name="note_1">(1)</a><i>The usage of Class Variables (starting with @@) has been discussed a lot on the Ruby-Talk ML and I adhere to a vast group of Rubyist who discourage their usage.</i><br /><hr /><br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">Can PPP do this too?</span></span><br /><br />Well it can, now, but you have not seen the code yet;). Let us have a look how it is used before explaining the implementation. The code below is the equivalent to the above code but expressed in <b>PPP</b>.<br /><!-- ruby2html inlined source from ppp-properties-use-case.rb --><br /><pre><span class="ident">require</span> <span class="punct">'</span><span class="string">ppp</span><span class="punct">'</span><br /><br /><span class="ident">person</span> <span class="punct">=</span> <span class="ident">new_proto</span> <span class="punct">{</span><br /><span class="ident">proto_variable</span> <span class="symbol">:count</span><span class="punct">,</span> <span class="number">0</span><br /><span class="keyword">def </span><span class="method">init</span> <span class="ident">name</span><span class="punct">,</span> <span class="ident">age</span><span class="punct">=</span><span class="constant">nil</span><br /> <span class="ident">obj_variable</span> <span class="symbol">:name</span><span class="punct">,</span> <span class="ident">name</span><br /> <span class="ident">obj_variable</span> <span class="symbol">:age</span><span class="punct">,</span> <span class="ident">age</span><br /> <span class="constant">self</span><span class="punct">.</span><span class="ident">count</span> <span class="punct">+=</span> <span class="number">1</span><br /><span class="keyword">end</span><br /><span class="punct">}</span><br /><br /><span class="ident">spencer</span> <span class="punct">=</span> <span class="ident">person</span><span class="punct">.</span><span class="ident">new</span> <span class="punct">"</span><span class="string">Tracy</span><span class="punct">",</span> <span class="number">107</span><br /><span class="ident">puts</span> <span class="ident">spencer</span><span class="punct">.</span><span class="ident">count</span> <span class="comment"># -> 1</span><br /><span class="ident">hepburn</span> <span class="punct">=</span> <span class="ident">person</span><span class="punct">.</span><span class="ident">new</span> <span class="punct">"</span><span class="string">Katharine</span><span class="punct">"</span><br /><span class="ident">puts</span> <span class="ident">person</span><span class="punct">.</span><span class="ident">count</span> <span class="comment"># -> 2</span></pre><br />There are some differences, as a matter of fact <i>per object variables</i> and <i>per prototype variables</i> are syntactically identical, please note too, that, like in Pure Ruby, you cannot assign to a prototype without an explicit receiver, hence the <tt>self.count += 1</tt> above, the same has to be done for object variables:<br /><!-- ruby2html :start registers processor --><br /><!-- ruby2html :end triggers processor --><br /><pre><span class="ident">person</span><span class="punct">.</span><span class="ident">ext_proto</span> <span class="punct">{</span><br /> <span class="keyword">def </span><span class="method">birthday</span><br /> <span class="constant">self</span><span class="punct">.</span><span class="ident">age</span> <span class="punct">+=</span> <span class="number">1</span><br /> <span class="keyword">end</span><br /><span class="punct">}</span></pre><br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">Looking under the Hood</span></span><br /><br />I decided to use closures, I just dislike the <tt>@</tt> sign I guess;)<br /><!-- ruby2html :start registers processor --><br /><!-- ruby2html :end triggers processor --><br /><pre><span class="keyword">class </span><span class="class">Object</span><br /><span class="keyword">def </span><span class="method">singleton</span><span class="punct">;</span> <span class="keyword">class </span><span class="punct"><<</span> <span class="constant">self</span><span class="punct">;</span> <span class="constant">self</span> <span class="keyword">end</span> <span class="keyword">end</span><br /><span class="keyword">end</span></pre><br />First of all I monkey-patched Object with the <tt>singleton</tt> method above, this is used very frequently by many Metaprogramming Gurus, so I need that too. Seriously you will see soon why this is useful.<br /><!-- ruby2html :start registers processor --><br /><!-- ruby2html :end triggers processor --><br /><pre><span class="keyword">class </span><span class="class">Prototype</span><br /><span class="keyword">def </span><span class="method">obj_variable</span> <span class="ident">varname</span><span class="punct">,</span> <span class="ident">value</span><span class="punct">,</span> <span class="ident">readonly</span> <span class="punct">=</span> <span class="constant">false</span><br /> <span class="ident">variable</span> <span class="punct">=</span> <span class="ident">value</span><br /> <span class="ident">singleton</span><span class="punct">.</span><span class="ident">instance_eval</span> <span class="keyword">do</span><br /> <span class="ident">define_method</span> <span class="ident">varname</span> <span class="keyword">do</span> <span class="ident">variable</span> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /> <span class="keyword">return</span> <span class="keyword">if</span> <span class="ident">readonly</span><br /> <span class="ident">singleton</span><span class="punct">.</span><span class="ident">instance_eval</span> <span class="keyword">do</span><br /> <span class="ident">define_method</span> <span class="punct">"</span><span class="string"><span class="expr">#{varname}</span>=</span><span class="punct">"</span> <span class="keyword">do</span> <span class="punct">|</span> <span class="ident">val</span> <span class="punct">|</span> <span class="ident">variable</span> <span class="punct">=</span> <span class="ident">val</span> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /><span class="keyword">end</span> <span class="comment"># def obj_variable varname, value, readonly = false</span><br /><span class="keyword">end</span></pre><br />Then the definition of <tt>obj_variable</tt> goes right into the <tt>Prototype</tt> class, that is why we use it in the <tt>init</tt> method and not inside the prototype block.<br />Please note the usage of the closure. The local variable called <tt>variable</tt> is created in the first line of the method and then we use two metaprogramming tools, <i>instance_eval</i> and <i>define_method</i> to define the accessor methods to the <i>property</i>. As both of these use blocks we have created a closure to access the local variable <tt>variable</tt>. Nobody can take this away from us anymore.<br /><!-- ruby2html :start registers processor --><br /><!-- ruby2html :end triggers processor --><br /><pre><span class="keyword">class </span><span class="class">Module</span><br /><span class="keyword">def </span><span class="method">proto_variable</span> <span class="ident">varname</span><span class="punct">,</span> <span class="ident">value</span><span class="punct">,</span> <span class="ident">readonly</span><span class="punct">=</span><span class="constant">false</span><br /> <span class="ident">variable</span> <span class="punct">=</span> <span class="ident">value</span><br /> <span class="ident">define_method</span> <span class="ident">varname</span> <span class="keyword">do</span> <span class="ident">variable</span> <span class="keyword">end</span><br /> <span class="keyword">return</span> <span class="keyword">if</span> <span class="ident">readonly</span><br /> <span class="ident">define_method</span> <span class="punct">"</span><span class="string"><span class="expr">#{varname}</span>=</span><span class="punct">"</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">val</span><span class="punct">|</span> <span class="ident">variable</span> <span class="punct">=</span> <span class="ident">val</span> <span class="keyword">end</span><br /><span class="keyword">end</span> <span class="comment"># def proto_variable varname, value</span><br /><span class="keyword">end</span> <span class="comment"># class Module</span></pre><br />The definition of <tt>proto_variable</tt> has to go into <tt>Module</tt> of course. That is the context in which the block of <i>new_proto</i> or <i>ext_proto</i> is interpreted in.<br />Note the same technique as above only that we do not need the singleton here, we want the method to be defined in the prototype itself as this behavior is shared between the prototype and its objects.<br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">Is This Good For Something?</span></span><br />Sure, allows me to write a Blog :). But maybe I can convince you of the Beauty and Usefulness of the PPP by means of the example of the <i>open-proto</i> implementation.<br />The <i>open-proto</i> implementation mimics <i>open-struct</i> and Facet's <i>open-object</i> behavior. Properties can be created on the fly:<br /><!-- ruby2html inlined source from open-proto-usecase.rb --><br /><pre><span class="ident">require</span> <span class="punct">'</span><span class="string">labrador/exp/open-proto</span><span class="punct">'</span><br /><br /><span class="ident">my_open</span> <span class="punct">=</span> <span class="ident">new_proto</span><span class="punct">(</span><span class="constant">Prototype</span><span class="punct">::</span><span class="constant">OpenProto</span><span class="punct">)</span> <span class="punct">{</span><br /><span class="keyword">def </span><span class="method">greeting</span><br /> <span class="ident">puts</span> <span class="punct">"</span><span class="string">I am <span class="expr">#{name}</span> and I have a value of <span class="expr">#{value}</span></span><span class="punct">"</span><br /><span class="keyword">end</span><br /><span class="punct">}</span><br /><span class="ident">my_object</span> <span class="punct">=</span> <span class="ident">my_open</span><span class="punct">.</span><span class="ident">new</span> <span class="symbol">:name</span> <span class="punct">=></span> <span class="punct">"</span><span class="string">Fourty Two</span><span class="punct">"</span><br /><span class="ident">puts</span> <span class="ident">my_object</span><span class="punct">.</span><span class="ident">name</span><br /><span class="ident">my_object</span><span class="punct">.</span><span class="ident">value</span> <span class="punct">=</span> <span class="number">222</span><br /><br /><span class="ident">another</span> <span class="punct">=</span> <span class="ident">my_object</span><span class="punct">.</span><span class="ident">new</span> <span class="symbol">:name</span> <span class="punct">=></span> <span class="punct">"</span><span class="string">Fourty Six</span><span class="punct">",</span> <span class="symbol">:value</span> <span class="punct">=></span> <span class="number">42</span><br /><span class="ident">my_object</span><span class="punct">.</span><span class="ident">greeting</span><br /><span class="ident">another</span><span class="punct">.</span><span class="ident">greeting</span><br /><br /></pre><br />Properties are defined on the fly, on a per object base and either by assignment or in the constructor.<br />It amazed myself how easy it was to implement this behavior (as it is in Pure Ruby I admit ;).<br /><!-- ruby2html inlined source from open-proto.rb --><br /><pre><span class="keyword">class </span><span class="class">Prototype</span><br /><span class="constant">OpenProto</span> <span class="punct">=</span> <span class="ident">new_proto</span> <span class="keyword">do</span><br /> <span class="keyword">def </span><span class="method">init</span> <span class="ident">params</span><span class="punct">={}</span><br /> <span class="ident">params</span><span class="punct">.</span><span class="ident">each_pair</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">k</span><span class="punct">,</span><span class="ident">v</span><span class="punct">|</span><br /> <span class="ident">obj_variable</span> <span class="ident">k</span><span class="punct">,</span> <span class="ident">v</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /><br /> <span class="keyword">def </span><span class="method">method_missing</span> <span class="ident">name</span><span class="punct">,</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">blk</span><br /> <span class="keyword">super</span> <span class="ident">name</span><span class="punct">,</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">blk</span> <span class="keyword">if</span><br /> <span class="ident">args</span><span class="punct">.</span><span class="ident">size</span> <span class="punct">!=</span> <span class="number">1</span> <span class="keyword">or</span><br /> <span class="ident">blk</span> <span class="keyword">or</span><br /> <span class="ident">name</span><span class="punct">.</span><span class="ident">to_s</span><span class="punct">[-</span><span class="number">1</span><span class="punct">]</span> <span class="punct">!=</span> <span class="char">?=</span><br /> <span class="ident">obj_variable</span> <span class="ident">name</span><span class="punct">.</span><span class="ident">to_s</span><span class="punct">[</span><span class="number">0</span><span class="punct">..-</span><span class="number">2</span><span class="punct">],</span> <span class="ident">args</span><span class="punct">.</span><span class="ident">first</span><br /> <span class="keyword">end</span><br /><span class="keyword">end</span><br /><span class="keyword">end</span></pre><br />I make this 17 lines of Ruby Code.<br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">Conclusion:</span></span><br /><br />I hope you have enjoyed the journey, gentle reader, if you would like to play around with this programming style maybe you would like to have a look at <b>Labrador</b> as <i>PPP</i> and <i>open-proto</i> are part of version 0.1 of the <i>Lazy Programmer's Best Friend</i>.<br />I have uploaded the two files <tt>labrador/exp/ppp</tt> and <tt>labrador/exp/open-proto</tt> in an extra file called <i>labrador-experimental-0.1</i> you can download all that from <a href="http://rubyforge.org/projects/labrador">Rubyforge</a>.Robert Doberhttp://www.blogger.com/profile/06304126315464640783noreply@blogger.com0tag:blogger.com,1999:blog-6539205209092092742.post-40283188254183245892007-08-11T18:51:00.000+02:002007-08-13T22:35:33.390+02:00And this week, Prototypes, The ConclusionLast time I followed a path I was put on by the excellent work of Ara T. Howard, and I stopped at a very naive implementation of the Prototype Programming Paradigm. (Yet another PPP;).<br /><br />It was the fast (and late) conclusion of some days of refelection and testing of ideas, it is far from ideal...<br />The thing that disturbes me most is that I tempted to deal with data ( c.f. Prototype#deepdup), and I did not even do it well. <br />Dealing with data is a bad idea anyway, we are talking <b>behavior</b> here, and data should not influence the conceptional decisions to be taken.<br /><br />Freed from any data concerns I came up with the following code, but before throwing it at you, gentle reader, I would like to comment the concept I am following here.<br />In order to avoid unneccessary confusion (I will not avoid necessary confusion, those who read my posts at ruby-talk know why ;), I will use <i>Prototype</i> (capitalized) whenever I refer to the implementation ( a Ruby class ), and <i>prototype</i> (lowercase) whenever I refer to a prototyped object (an instance of <i>Prototype</i> as a matter of fact).<br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">And What Is A Prototype Anyway?</span></span><br /><br />A <b>prototype</b> is an object that has its behavior closely attached to itself. It playes the role of a class and an instance of that class at the same time.<br />Remember, it was the main motivation of my last post to get rid of classes at all in our programs. <br />I losely follow a definition of <i>prototype</i> as defined <a href="http://en.wikipedia.org/wiki/Prototype-based_programming">here</a>.<br /><br />It is the <i>Prototype</i> class, yes we use classes for implementation of this nevertheless ;), that will take care of gluing behavior into the <i>prototype</i> object.<br />The basic instrument to do so is the <tt>__proto__</tt> attribute <i>Prototype</i> defines for its instances.<br />We will see shortly that <tt>__proto__</tt> is indeed a <tt>Module</tt> that will be included into new modules replacing it as soon as a prototype will be extended with new functionality.<br /><br />It is important to remember that Ara T. Howard and Pit Capitain had to do very much work (as interesting and sophisticated it was) to allow for dynamic method redefinition for class based methods.<br />Our classless approach rips this burden off our shoulders of course, all methods a <i>prototype</i> responds to are module based (via the standard Ruby <tt>extend</tt>).<br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">Inheritance & Method Resolution Order</span></span><br /><br />Inheritance is therefore implemented by method inclusion into the <tt>__proto__</tt> attribute of a <i>prototype</i>.<br /><br />We have a <i>prototype</i> <tt>p</tt> which has stored its behavior in its <tt>__proto__</tt> attribute. This is indeed an <b>invariant</b> of this PPP implementation: <br />A <i>prototype</i> is always extended by the module kept in its <tt>__proto__</tt> attribute.<br />Whenever we want to extend a <tt>p</tt> with some new behavior it is done via a new module, either given as a module or by constructing one on the fly from a given block.<br />This new module will become the value ot the <tt>p's __proto__</tt> attribute but not before including the value of the old value of the <i>prototype's</i> <tt>__proto__</tt> attribute into the module. Thus the old behavior is not lost.<br />The code for doing this is factorized into <i>Prototype's</i> private <tt>extend_one</tt> method and is simple enough:<br /><!-- ruby2html inlined source from ppp-prototype-extend-one.rb --><br /><pre> <span class="keyword">def </span><span class="method">extend_one</span> <span class="ident">mod</span><br /> <span class="ident">proto</span> <span class="punct">=</span> <span class="ident">__proto__</span><br /> <span class="ident">mod</span><span class="punct">.</span><span class="ident">instance_eval</span> <span class="punct">{</span> <span class="ident">include</span> <span class="ident">proto</span> <span class="punct">}</span> <span class="keyword">if</span> <span class="ident">proto</span><br /> <span class="constant">self</span><span class="punct">.</span><span class="ident">__proto__</span> <span class="punct">=</span> <span class="ident">mod</span><br /> <span class="ident">extend</span> <span class="ident">mod</span><br /> <span class="keyword">end</span></pre><br /><br />As mentioned above the <i>Prototype</i> class implements an attribute <tt>__proto__</tt>. The constructor allows us to indicate behavior to be inherited as follows, a list of modules ( of whihch the last will be the last in the inheritance chain ) and an optional block of which a module will be constructed.<br />The module constructed from the block is the last to be inherited into the <tt>__proto__</tt> chain.<br /><br />The following example shows the method resolution order as described above.<br /><!-- ruby2html :start registers processor --><br /><!-- ruby2html :end triggers processor --><br /><pre> <span class="keyword">module </span><span class="module">A</span><br /> <span class="keyword">def </span><span class="method">a</span><span class="punct">;</span> <span class="number">42</span> <span class="keyword">end</span><br /> <span class="keyword">def </span><span class="method">b</span><span class="punct">;</span> <span class="number">46</span> <span class="keyword">end</span><br /> <span class="keyword">def </span><span class="method">c</span><span class="punct">;</span> <span class="number">39</span> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /> <span class="keyword">module </span><span class="module">B</span><br /> <span class="keyword">def </span><span class="method">b</span><span class="punct">;</span> <span class="number">52</span> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /> <span class="ident">p</span> <span class="punct">=</span> <span class="constant">Prototype</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(</span> <span class="constant">A</span><span class="punct">,</span> <span class="constant">B</span> <span class="punct">){</span><br /> <span class="keyword">def </span><span class="method">c</span><span class="punct">;</span> <span class="number">60</span> <span class="keyword">end</span><br /> <span class="punct">}</span><br /><br /> <span class="ident">puts</span> <span class="ident">p</span><span class="punct">.</span><span class="ident">a</span> <span class="comment"># --> 42</span><br /> <span class="ident">puts</span> <span class="ident">p</span><span class="punct">.</span><span class="ident">b</span> <span class="comment"># --> 52</span><br /> <span class="ident">puts</span> <span class="ident">p</span><span class="punct">.</span><span class="ident">c</span> <span class="comment"># --> 60</span><br /></pre><br />And here goes the definition of <tt>Prototype#initialize</tt> which does the work to arrive at the result shown above:<br /><!-- ruby2html :start registers processor --><br /><!-- ruby2html :end triggers processor --><br /><pre><span class="constant">PrototypeError</span> <span class="punct">=</span> <span class="constant">Class</span><span class="punct">::</span><span class="ident">new</span> <span class="constant">StandardError</span><br /><br /><span class="keyword">class </span><span class="class">Prototype</span><br /> <span class="ident">attr_accessor</span> <span class="symbol">:__proto__</span><br /> <span class="keyword">def </span><span class="method">initialize</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">blk</span><br /> <span class="ident">ext_proto</span> <span class="punct">*</span><span class="ident">args</span><br /> <span class="ident">ext_proto</span> <span class="constant">Module</span><span class="punct">::</span><span class="ident">new</span><span class="punct">(</span> <span class="punct">&</span><span class="ident">blk</span> <span class="punct">)</span> <span class="keyword">if</span> <span class="ident">blk</span><br /> <span class="keyword">end</span> <span class="comment"># def initialize *args, &blk</span><br /> <span class="punct">...</span><br /><span class="keyword">end</span> <span class="comment"># class Prototype</span></pre><br /><br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">How To Use It, The Prototype API</span></span><br /><br />We want of course some simple way to create <i>prototypes</i> new clones or copies of a <i>prototype</i> or to extend <i>prototypes</i>.<br />The following interface methods can be used for this task:<br /><!-- ruby2html :start registers processor --><br /><!-- ruby2html :end triggers processor --><br /><pre><span class="keyword">class </span><span class="class">Prototype</span><br /> <br /> <span class="keyword">def </span><span class="method">ext_proto</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">blk</span><br /> <span class="ident">args</span><span class="punct">.</span><span class="ident">each</span> <span class="keyword">do</span><br /> <span class="punct">|</span> <span class="ident">arg</span> <span class="punct">|</span><br /> <span class="keyword">case</span> <span class="ident">arg</span><br /> <span class="keyword">when</span> <span class="constant">Module</span><br /> <span class="ident">extend_one</span> <span class="ident">arg</span><br /> <span class="keyword">else</span><br /> <span class="keyword">begin</span><br /> <span class="ident">extend_one</span> <span class="ident">arg</span><span class="punct">.</span><span class="ident">__proto__</span><br /> <span class="keyword">rescue</span> <span class="constant">NoMethodError</span><br /> <span class="keyword">raise</span> <span class="constant">PrototypeError</span><span class="punct">,</span><br /> <span class="punct">"</span><span class="string">Cannot extend an object with a non Module or non Prototype object</span><span class="punct">"</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span> <span class="comment"># case arg</span><br /> <span class="keyword">end</span> <span class="comment"># args.each</span><br /> <span class="ident">extend_one</span> <span class="constant">Module</span><span class="punct">::</span><span class="ident">new</span><span class="punct">(</span> <span class="punct">&</span><span class="ident">blk</span> <span class="punct">)</span> <span class="keyword">if</span> <br /> <span class="ident">blk</span> <span class="keyword">or</span> <span class="ident">args</span><span class="punct">.</span><span class="ident">empty?</span><br /> <span class="constant">self</span><br /> <span class="keyword">end</span> <span class="comment"># def ext_proto</span><br /><span class="keyword">end</span> <span class="comment"># class Prototype</span><br /><br /><span class="keyword">module </span><span class="module">Kernel</span><br /> <span class="keyword">def </span><span class="method">new_proto</span> <span class="ident">superobject</span><span class="punct">=</span><span class="constant">nil</span><span class="punct">,</span> <span class="punct">*</span><span class="ident">mods</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">blk</span><br /> <span class="keyword">begin</span><br /> <span class="ident">superobject</span><span class="punct">.</span><span class="ident">dup</span><br /> <span class="keyword">rescue</span> <span class="constant">TypeError</span><span class="punct">,</span> <span class="constant">NoMethodError</span><br /> <span class="constant">Prototype</span><span class="punct">.</span><span class="ident">new</span><br /> <span class="keyword">end</span><span class="punct">.</span><span class="ident">ext_proto</span><span class="punct">(</span> <span class="punct">*</span><span class="ident">mods</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">blk</span> <span class="punct">)</span><br /> <span class="keyword">end</span><br /><span class="keyword">end</span> <span class="comment"># module Kernel</span></pre><br /> Thus we will be allowed to create a new <i>prototype</i> with <br /><!-- ruby2html :start registers processor --><br /><!-- ruby2html :end triggers processor --><br /><pre> <span class="ident">new_proto</span> <span class="constant">nil</span><span class="punct">,</span> <span class="constant">Comparable</span> <span class="keyword">do</span><br /> <span class="keyword">def </span><span class="method"><=></span> <span class="ident">other</span><br /> <span class="punct">...</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span> </pre><br /> Or we might like to write more explicit code:<br /><!-- ruby2html :start registers processor --><br /><!-- ruby2html :end triggers processor --><br /><pre><br /> <span class="keyword">module </span><span class="module">Printable</span><br /> <span class="keyword">def </span><span class="method">to_s</span><span class="punct">;</span> <span class="ident">inspect</span> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /><br /> <span class="ident">sortable</span> <span class="punct">=</span> <span class="ident">new_proto</span><br /> <span class="ident">sortable</span><span class="punct">.</span><span class="ident">ext_proto</span> <span class="constant">Printable</span><span class="punct">,</span> <span class="constant">Comparable</span> <span class="keyword">do</span><br /> <span class="keyword">def </span><span class="method"><=></span> <span class="ident">other</span><br /> <span class="ident">name</span> <span class="punct"><=></span> <span class="ident">other</span><span class="punct">.</span><span class="ident">name</span><br /> <span class="keyword">end</span><br /> <span class="ident">attr_accessor</span> <span class="symbol">:name</span><br /> <span class="keyword">def </span><span class="method">init</span> <span class="ident">name</span><br /> <span class="constant">self</span><span class="punct">.</span><span class="ident">name</span> <span class="punct">=</span> <span class="ident">name</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /><br /> <span class="ident">puts</span> <span class="punct">[</span><span class="ident">sortable</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(</span> <span class="punct">"</span><span class="string">Vilma</span><span class="punct">"</span> <span class="punct">),</span> <br /> <span class="ident">sortable</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(</span> <span class="punct">"</span><span class="string">Angie</span><span class="punct">"</span> <span class="punct">)</span> <span class="punct">].</span><span class="ident">sort</span><br /><span class="comment">##<Prototype:0x2b64110 @name="Angie", @__proto__=#<Module:0x2b643f4>></span><br /><span class="comment">##<Prototype:0x2b641c4 @name="Vilma", @__proto__=#<Module:0x2b643f4>></span></pre><br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">No More Classes! No More Modules?</span></span><br /><br />Although I do not see any technical problem to use modules directly, it might be more consistent an approach not to do so anywmore.<br />We have got rid of <b>class</b> why not get rid of <b>module</b> too? This kind of paradigm change can do a lot to your programming style.<br /><br />The alternative would be to use <b>prototypes</b> and anonymous module blocks exclusively (still being able to do a "<tt>include mod</tt>" in the block of course).<br />In this alternative way the code above would look as follows:<br /><!-- ruby2html :start registers processor --><br /><!-- ruby2html :end triggers processor --><br /><pre><span class="ident">printable</span> <span class="punct">=</span> <span class="constant">Prototype</span><span class="punct">.</span><span class="ident">new</span> <span class="punct">{</span><br /> <span class="keyword">def </span><span class="method">to_s</span><span class="punct">;</span> <span class="ident">inspect</span> <span class="keyword">end</span><br /><span class="punct">}</span><br /><span class="ident">sortable</span> <span class="punct">=</span> <span class="ident">new_proto</span> <span class="ident">printable</span><span class="punct">,</span> <span class="constant">Comparable</span> <span class="keyword">do</span><br /> <span class="punct">...</span></pre><br />Note that we still allow modules in the inheritance chain, this seems quite useful for the usage of predefined modules.<br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">And What About Object Initialization?</span></span><br />This is the last missing piece of our puzzle, <i>Prototype</i> defines a <tt>new</tt> method that will call an <tt>init</tt> method for the newly created object, if it exits that is of course. <br />Not much magic here<br /><!-- ruby2html :start registers processor --><br /><!-- ruby2html :end triggers processor --><br /><pre><span class="keyword">class </span><span class="class">Prototype</span><br /> <span class="keyword">def </span><span class="method">new</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">block</span><br /> <span class="ident">o</span> <span class="punct">=</span> <span class="ident">dup</span><br /> <span class="ident">o</span><span class="punct">.</span><span class="ident">extend</span> <span class="ident">__proto__</span><br /> <span class="ident">o</span><span class="punct">.</span><span class="ident">instance_eval</span> <span class="punct">{</span> <br /> <span class="ident">init</span><span class="punct">(</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">block</span> <span class="punct">)</span><br /> <span class="punct">}</span> <span class="keyword">if</span> <span class="ident">respond_to?</span> <span class="symbol">:init</span><br /> <span class="ident">o</span><br /> <span class="keyword">end</span> <span class="comment"># def new *args, &block</span><br /><span class="keyword">end</span> <span class="comment"># class Prototype</span></pre><br />Please note that our only concern is to disassociate the new object from the generating <i>prototype</i> object. It is completely up to the user to manage the data, data can be shared between a generator object and a generated object by means of references pointed to by instance variables defined for the generator.<br />Example for shared data between generator prototype and generated prototype:<br /><!-- ruby2html :start registers processor --><br /><!-- ruby2html :end triggers processor --><br /><pre> <span class="ident">printable</span> <span class="punct">=</span> <span class="constant">Prototype</span><span class="punct">.</span><span class="ident">new</span> <span class="punct">{</span><br /> <span class="keyword">def </span><span class="method">to_s</span><span class="punct">;</span> <span class="ident">inspect</span> <span class="keyword">end</span><br /> <span class="punct">}</span><br /><br /> <span class="ident">dog</span> <span class="punct">=</span> <span class="ident">new_proto</span> <span class="ident">printable</span> <span class="keyword">do</span><br /> <span class="ident">attr_accessor</span> <span class="symbol">:name</span><br /> <span class="keyword">def </span><span class="method">init</span> <span class="ident">name</span><span class="punct">=</span><span class="constant">nil</span><br /> <span class="constant">self</span><span class="punct">.</span><span class="ident">name</span> <span class="punct">=</span> <span class="ident">name</span> <span class="keyword">if</span> <span class="ident">name</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /> <span class="ident">vilma</span> <span class="punct">=</span> <span class="ident">dog</span><span class="punct">.</span><span class="ident">new</span> <span class="punct">%q{</span><span class="string">vilma</span><span class="punct">}</span><br /> <span class="ident">angie</span> <span class="punct">=</span> <span class="ident">vilma</span><span class="punct">.</span><span class="ident">new</span> <br /> <span class="ident">angie</span><span class="punct">.</span><span class="ident">name</span> <span class="punct"><<</span> <span class="punct">"</span><span class="string">& angie</span><span class="punct">"</span><br /><br /> <span class="ident">puts</span> <span class="ident">vilma</span> <br /> <span class="ident">puts</span> <span class="ident">angie</span><br /><span class="comment">##<Prototype:0x2b64304 @__proto__=#<Module:0x2b64458>, @name="vilma& angie"></span><br /><span class="comment">##<Prototype:0x2b6428c @__proto__=#<Module:0x2b64458>, @name="vilma& angie"></span><br /></pre><br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">The PPP Implementation</span></span><br />Here goes the whole implementation:<br /><br /><!-- ruby2html inlined source from ppp-implementation.rb --><br /><pre><span class="constant">PrototypeError</span> <span class="punct">=</span> <span class="constant">Class</span><span class="punct">::</span><span class="ident">new</span> <span class="constant">StandardError</span><br /><br /><span class="keyword">class </span><span class="class">Prototype</span><br /> <span class="ident">attr_accessor</span> <span class="symbol">:__proto__</span><br /> <span class="keyword">def </span><span class="method">initialize</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">blk</span><br /> <span class="ident">ext_proto</span> <span class="punct">*</span><span class="ident">args</span><br /> <span class="ident">ext_proto</span> <span class="constant">Module</span><span class="punct">::</span><span class="ident">new</span><span class="punct">(</span> <span class="punct">&</span><span class="ident">blk</span> <span class="punct">)</span> <span class="keyword">if</span> <span class="ident">blk</span><br /> <span class="keyword">end</span> <span class="comment"># def initialize *args, &blk</span><br /><br /> <span class="keyword">def </span><span class="method">ext_proto</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">blk</span><br /> <span class="ident">args</span><span class="punct">.</span><span class="ident">each</span> <span class="keyword">do</span><br /> <span class="punct">|</span> <span class="ident">arg</span> <span class="punct">|</span><br /> <span class="keyword">case</span> <span class="ident">arg</span><br /> <span class="keyword">when</span> <span class="constant">Module</span><br /> <span class="ident">extend_one</span> <span class="ident">arg</span><br /> <span class="keyword">else</span><br /> <span class="keyword">begin</span><br /> <span class="ident">extend_one</span> <span class="ident">arg</span><span class="punct">.</span><span class="ident">__proto__</span><br /> <span class="keyword">rescue</span> <span class="constant">NoMethodError</span><br /> <span class="keyword">raise</span> <span class="constant">PrototypeError</span><span class="punct">,</span><br /> <span class="punct">"</span><span class="string">Cannot extend an object with a non Module or non Prototype object</span><span class="punct">"</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span> <span class="comment"># case arg</span><br /> <span class="keyword">end</span> <span class="comment"># args.each</span><br /> <span class="ident">extend_one</span> <span class="constant">Module</span><span class="punct">::</span><span class="ident">new</span><span class="punct">(</span> <span class="punct">&</span><span class="ident">blk</span> <span class="punct">)</span> <span class="keyword">if</span> <span class="ident">blk</span> <span class="keyword">or</span> <span class="ident">args</span><span class="punct">.</span><span class="ident">empty?</span><br /> <span class="constant">self</span><br /> <span class="keyword">end</span> <span class="comment"># def ext_proto</span><br /><br /> <span class="keyword">def </span><span class="method">new</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">block</span><br /> <span class="ident">o</span> <span class="punct">=</span> <span class="ident">dup</span><br /> <span class="ident">o</span><span class="punct">.</span><span class="ident">extend</span> <span class="ident">__proto__</span><br /> <span class="ident">o</span><span class="punct">.</span><span class="ident">instance_eval</span> <span class="punct">{</span> <br /> <span class="ident">init</span><span class="punct">(</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">block</span> <span class="punct">)</span><br /> <span class="punct">}</span> <span class="keyword">if</span> <span class="ident">respond_to?</span> <span class="symbol">:init</span><br /> <span class="ident">o</span><br /> <span class="keyword">end</span> <span class="comment"># def new *args, &block</span><br /><br /> <span class="ident">private</span><br /> <span class="keyword">def </span><span class="method">extend_one</span> <span class="ident">mod</span><br /> <span class="ident">proto</span> <span class="punct">=</span> <span class="ident">__proto__</span><br /> <span class="ident">mod</span><span class="punct">.</span><span class="ident">instance_eval</span> <span class="punct">{</span> <span class="ident">include</span> <span class="ident">proto</span> <span class="punct">}</span> <span class="keyword">if</span> <span class="ident">proto</span><br /> <span class="constant">self</span><span class="punct">.</span><span class="ident">__proto__</span> <span class="punct">=</span> <span class="ident">mod</span><br /> <span class="ident">extend</span> <span class="ident">mod</span><br /> <span class="keyword">end</span><br /><span class="keyword">end</span><br /><br /><span class="keyword">module </span><span class="module">Kernel</span><br /> <span class="keyword">def </span><span class="method">new_proto</span> <span class="ident">superobject</span><span class="punct">=</span><span class="constant">nil</span><span class="punct">,</span> <span class="punct">*</span><span class="ident">mods</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">blk</span><br /> <span class="keyword">begin</span><br /> <span class="ident">superobject</span><span class="punct">.</span><span class="ident">dup</span><br /> <span class="keyword">rescue</span> <span class="constant">TypeError</span><span class="punct">,</span> <span class="constant">NoMethodError</span><br /> <span class="constant">Prototype</span><span class="punct">.</span><span class="ident">new</span><br /> <span class="keyword">end</span><span class="punct">.</span><span class="ident">ext_proto</span><span class="punct">(</span> <span class="punct">*</span><span class="ident">mods</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">blk</span> <span class="punct">)</span><br /> <span class="keyword">end</span><br /><span class="keyword">end</span> <span class="comment"># module Kernel</span><br /></pre><br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">Performance?</span></span><br /><br />Performance is bad of course, <i>Prototype</i> based code is about 10 times slower than class based code, as my first benchmarks have shown.Robert Doberhttp://www.blogger.com/profile/06304126315464640783noreply@blogger.com1tag:blogger.com,1999:blog-6539205209092092742.post-84029315758465595372007-08-01T21:14:00.001+02:002007-08-02T22:54:45.390+02:00Classless Ruby, a Paradigm Change?<span style="font-weight: bold;"><span style="font-size:130%;">What are classes for in Ruby?</span><br /></span><br />That seems quite a stupid question at first, let us see if we can work to a point where the question makes more sense.<br /><br />The whole thing started in my mind by a recent post of Ara T. Howard, implementing a neat meta-programming trick allowing to redefine methods.<br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">Method Redefinition</span></span><br />The basic idea was to allow to redefine a method in a class by still having access to the redefined method via the super call.<br />Without looking at the implementation this is what Ara wanted to achieve.<br /><br />The implementation, heavily influenced by Pit Captain, can be found here, have a look <a href="http://drawohara.tumblr.com/post/7241442">here</a> if you are interested in Ruby Metaprogramming, it is really top notch code.<br /><!-- ******************************************************** Begin Code: Ara's example *********************************************************** --><br /><pre><span class="keyword">class </span><span class="class">C</span><br /><span class="keyword"> def </span><span class="method">foo</span><span class="punct">()</span> <span class="punct">'</span><span class="string">f</span><span class="punct">'</span> <span class="keyword">end</span><br /><span class="keyword"> def </span><span class="method">bar</span><span class="punct">()</span> <span class="punct">'</span><span class="string">b</span><span class="punct">'</span> <span class="keyword">end</span><br /><span class="keyword"> def </span><span class="method">foobar</span><span class="punct">()</span> <span class="ident">foo</span> <span class="punct">+</span> <span class="ident">bar</span> <span class="keyword">end</span><br /><span class="keyword">end</span><br /><br /><span class="ident">c</span> <span class="punct">=</span> <span class="constant">C</span><span class="punct">.</span><span class="ident">new</span><br /><span class="ident">p</span> <span class="ident">c</span><span class="punct">.</span><span class="ident">foobar</span> <span class="comment"># => "fb"</span><br /><br /><span class="keyword">class </span><span class="class">C</span><br /><span class="ident"> redefining</span> <span class="keyword">do</span><br /><span class="keyword"> def </span><span class="method">foo</span><span class="punct">()</span> <span class="keyword">super</span> <span class="punct">+</span> <span class="punct">'</span><span class="string">oo</span><span class="punct">'</span> <span class="keyword">end</span><br /><span class="keyword"> end</span><br /><span class="keyword">end</span><br /><br /><span class="ident">p</span> <span class="ident">c</span><span class="punct">.</span><span class="ident">foobar</span> <span class="comment"># => "foob"</span><br /><br /><span class="keyword">class </span><span class="class">C</span><br /><span class="ident"> redefining</span> <span class="keyword">do</span><br /><span class="keyword"> def </span><span class="method">bar</span><span class="punct">()</span> <span class="keyword">super</span> <span class="punct">+</span> <span class="punct">'</span><span class="string">ar</span><span class="punct">'</span> <span class="keyword">end</span><br /><span class="keyword"> end</span><br /><span class="keyword">end</span><br /><br /><span class="ident">p</span> <span class="ident">c</span><span class="punct">.</span><span class="ident">foobar</span> <span class="comment"># => "foobar"</span><br /><br /><span class="keyword">class </span><span class="class">C</span><br /><span class="ident"> redefining</span> <span class="keyword">do</span><br /><span class="keyword"> def </span><span class="method">foo</span><span class="punct">()</span> <span class="keyword">super</span><span class="punct">.</span><span class="ident">reverse</span> <span class="keyword">end</span><br /><span class="keyword"> def </span><span class="method">bar</span><span class="punct">()</span> <span class="keyword">super</span><span class="punct">.</span><span class="ident">reverse</span> <span class="keyword">end</span><br /><span class="keyword"> end</span><br /><span class="keyword">end</span><br /><br /><span class="ident">p</span> <span class="ident">c</span><span class="punct">.</span><span class="ident">foobar</span> <span class="comment"># => "oofrab"</span><br /></pre><br />It is indeed a nice feature to have, a very modular <span style="font-style: italic;">inheritance</span> mechanism. If you have looked at the implementation above, surely you have had a look at the implementation already;), you will see that it is rather complicated.<br />There are two reasons for that, firstly there is no way to tell Ruby to accept block parameters in a block, thus the following just will not be possible in Ruby 1.8, this will fortunately change in 1.9.<br /><br /><br /><pre><span class="ident">define_method</span><span class="punct">(</span> <span class="symbol">:my_method</span> <span class="punct">)</span> <span class="punct">{</span><br /><span class="punct"> |*</span><span class="ident">args</span><span class="punct">,</span> <span style="color:red;">&blk</span><span class="punct">|</span><br /><span class="punct"> ...</span><br /><span class="punct">}</span><br /></pre><br />And secondly there is a well defined behavior of directly defined methods being looked up before mixed-in methods.<br />When Ara published his redefining code first I had the urge to write this much, much simpler and I <span style="font-weight: bold;">almost</span> succeeded, actually I knew that the code was not doing what Ara wanted, but I felt that the simplicity of the solution was worth looking into it.<br />I also preferred to define the meta-code in class Module rather than in class Class.<br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">A Naive Approach</span></span><br /><br /><!-- ******************************************* Begin Code: My naive module inclusion Module#redefine ********************************************* --><br /><pre> <span class="keyword">class </span><span class="class">Module</span><br /><span class="keyword"> def </span><span class="method">redefine</span> <span class="punct">&</span><span class="ident">blk</span><br /><span class="ident"> include</span> <span class="constant">Module</span><span class="punct">::</span><span class="ident">new</span><span class="punct">(</span> <span class="punct">&</span><span class="ident">blk</span> <span class="punct">)</span><br /><span class="keyword"> end</span><br /><span class="keyword"> end</span><br /></pre><br /><br />Now this does some pretty neat tricks already, let us look at one example:<br /><pre> <span class="keyword">module </span><span class="module">M</span><br /><span class="keyword"> def </span><span class="method">m</span><span class="punct">;</span> <span class="number">1</span> <span class="keyword">end</span><br /><span class="keyword"> end</span><br /><br /><span class="keyword"> module </span><span class="module">N</span><br /><span class="ident"> include</span> <span class="constant">M</span><br /><span class="ident"> redefine</span> <span class="keyword">do</span><br /><span class="keyword"> def </span><span class="method">m</span><span class="punct">;</span> <span class="keyword">super</span> <span class="punct">+</span> <span class="number">1</span> <span class="keyword">end</span><br /><span class="keyword"> end</span><br /><span class="keyword"> end</span><br /><br /><span class="keyword"> class </span><span class="class">A</span><br /><span class="ident"> include</span> <span class="constant">N</span><br /><span class="ident"> redefine</span> <span class="keyword">do</span><br /><span class="keyword"> def </span><span class="method">m</span><span class="punct">;</span> <span class="keyword">super</span> <span class="punct">+</span> <span class="number">1</span> <span class="keyword">end</span><br /><span class="keyword"> end</span><br /><span class="keyword"> end</span><br /><br /><span class="ident"> puts</span> <span class="constant">A</span><span class="punct">.</span><span class="ident">new</span><span class="punct">.</span><span class="ident">m </span><span class="comment"> # --> 3</span><br /></pre><br />Unfortunately we cannot redefine a method that has been declared in a class as it will be looked up first, that is why Ara and Pit went through so much trouble to save the method, remove it from the class and put it on a call stack, they did not do that (only) for fun.<br />In order to demonstrate it change the definition of class A as follows<br /><!-- ****************************************************** Code Begin: Example for Naive Approach Not Working ****************************************************** --><br /> <br /><pre> <span class="ident">class</span> <span class="constant">A</span><br /> <span class="keyword">def </span><span class="method">m</span><span class="punct">;</span> <span class="number">41</span> <span class="keyword">end</span><br /> <span class="ident">redefine</span> <span class="keyword">do</span><br /> <span class="keyword">def </span><span class="method">m</span><span class="punct">;</span> <span class="keyword">super</span> <span class="punct">+</span> <span class="number">1</span> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /><span class="keyword">end</span><br /><br /><span class="ident">puts</span> <span class="constant">A</span><span class="punct">.</span><span class="ident">new</span><span class="punct">.</span><span class="ident">m</span><br /></pre><br /><br />The deception of not getting the right answer is great of course.<br />But how can we get 42? Yes, of course, by using Ara's and Pit's code...<br />Actually I fell in love with my naive code above.<br />I am well aware of its limitations, but why not just continue walking on this road? We will see where that gets us.<br />I was quite pleased with the journey.<br /><br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">Mixin Rules, Or Does It Not?</span> </span><br /><br />The first idea to allow redefinition is simply to use redefine for the definition of the methods in the class.<br />This would also allow to explicitly forbid redefinition for conventional methods, the following example does not even use redefine as it is just too simple, we explicitly mix an anonymous module in.<br /><!-- *************************************** Begin Code: Explicit Anonymous Mixin ***************************************** --><br /><pre> <span class="keyword">class </span><span class="class">Mixin</span><br /><span class="ident">include</span> <span class="constant">Module</span><span class="punct">::</span><span class="ident">new</span> <span class="punct">{</span><br /> <span class="ident">attr_reader</span> <span class="symbol">:a</span><br /> <span class="keyword">def </span><span class="method">initialize</span> <span class="ident">a</span><br /> <span class="attribute">@a</span> <span class="punct">=</span> <span class="ident">a</span><br /> <span class="keyword">end</span><br /> <span class="keyword">def </span><span class="method">increment</span><br /> <span class="attribute">@a</span> <span class="punct">+=</span> <span class="number">1</span><br /> <span class="keyword">end</span><br /><span class="punct">}</span><br /><span class="keyword">def </span><span class="method">old_style</span><span class="punct">;</span> <span class="punct">"</span><span class="string">old_style</span><span class="punct">"</span> <span class="keyword">end</span><br /><span class="keyword">end</span> <span class="comment"># class Mixin</span><br /><br /><span class="ident">m</span> <span class="punct">=</span> <span class="constant">Mixin</span><span class="punct">.</span><span class="ident">new</span> <span class="number">46</span><br /><span class="ident">puts</span> <span class="ident">m</span><span class="punct">.</span><span class="ident">a</span> <span class="comment"># --> 46</span><br /><span class="ident">m</span><span class="punct">.</span><span class="ident">increment</span><br /><span class="ident">puts</span> <span class="ident">m</span><span class="punct">.</span><span class="ident">a</span> <span class="comment"># --> 47</span><br /><span class="ident">puts</span> <span class="ident">m</span><span class="punct">.</span><span class="ident">old_style</span> <span class="comment"># --> "old_style"</span><br /><br /><span class="keyword">class </span><span class="class">Mixin</span><br /><span class="ident">include</span> <span class="constant">Module</span><span class="punct">::</span><span class="ident">new</span> <span class="punct">{</span><br /> <span class="keyword">def </span><span class="method">increment</span><br /> <span class="keyword">super</span><br /> <span class="attribute">@a</span> <span class="punct">+=</span> <span class="number">2</span><br /> <span class="keyword">end</span><br /><br /> <span class="keyword">def </span><span class="method">old_style</span><br /> <span class="keyword">super</span> <span class="punct"><<</span> <span class="punct">"</span><span class="string">::new_style</span><span class="punct">"</span><br /> <span class="keyword">end</span><br /><span class="punct">}</span><br /><span class="keyword">end</span><br /><br /><span class="ident">m</span><span class="punct">.</span><span class="ident">increment</span><br /><span class="ident">puts</span> <span class="ident">m</span><span class="punct">.</span><span class="ident">a</span> <span class="comment"># --> 50!!</span><br /><span class="ident">puts</span> <span class="ident">m</span><span class="punct">.</span><span class="ident">old_style</span> <span class="comment"><span style="color:red;"># --> "old_style"</span></span><br /></pre><br /><br />Let us imagine, for the sake of argument, that you like this idiom, are you going to write the following code pattern forever?<br /><br /><pre><br />class MyClass<br /> include Module::new {<br /> ...<br /></pre><br /><br />Probably not, why not mimic Ruby's class keyword a little bit and hide the implementation details in a Kernel method?<br /><!-- ******************************************************** Begin Code: Kernel#new_style_class ************************************************************* --><br /><pre><span class="keyword">module </span><span class="module">Kernel</span><br /><span class="keyword">def </span><span class="method">new_style_class</span> <span class="ident">name</span><span class="punct">,</span> <span class="ident">superclass</span><span class="punct">=</span><span class="constant">Object</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">block</span><br /> <span class="keyword">begin</span><br /> <span class="constant">Object</span><span class="punct">.</span><span class="ident">const_get</span><span class="punct">(</span> <span class="ident">name</span> <span class="punct">)</span><br /> <span class="keyword">rescue</span><br /> <span class="constant">Object</span><span class="punct">.</span><span class="ident">const_set</span><span class="punct">(</span> <span class="ident">name</span><span class="punct">,</span> <span class="constant">Class</span><span class="punct">::</span><span class="ident">new</span><span class="punct">(</span> <span class="ident">superclass</span> <span class="punct">)</span> <span class="punct">)</span><br /> <span class="keyword">end</span><span class="punct">.</span><br /> <span class="ident">instance_eval</span><span class="punct">{</span> <span class="ident">include</span> <span class="constant">Module</span><span class="punct">::</span><span class="ident">new</span><span class="punct">(</span> <span class="punct">&</span><span class="ident">block</span> <span class="punct">)</span> <span class="punct">}</span><br /><span class="keyword">end</span><br /><span class="keyword">end</span> <span class="comment"># module Kernel</span><br /><br /><span class="ident">new_style_class</span> <span class="symbol">:A</span> <span class="keyword">do</span><br /><span class="ident">attr_reader</span> <span class="symbol">:value</span><span class="punct">,</span> <span class="symbol">:version</span><br /><span class="keyword">def </span><span class="method">initialize</span><br /> <span class="attribute">@version</span><span class="punct">=</span><span class="number">1</span><br /> <span class="attribute">@value</span> <span class="punct">=</span><span class="number">0</span><br /><span class="keyword">end</span><br /><br /><span class="keyword">def </span><span class="method">set_value</span> <span class="ident">v</span><br /> <span class="attribute">@value</span> <span class="punct">=</span> <span class="ident">v</span><br /><span class="keyword">end</span><br /><span class="keyword">end</span><br /><br /><span class="ident">a</span> <span class="punct">=</span> <span class="constant">A</span><span class="punct">.</span><span class="ident">new</span><br /><span class="ident">puts</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">version</span><br /><span class="ident">a</span><span class="punct">.</span><span class="ident">set_value</span> <span class="number">52</span><br /><span class="ident">puts</span> <span class="ident">a</span><span class="punct">.</span><span class="ident">value</span><br /><br /><span class="ident">new_style_class</span> <span class="symbol">:A</span> <span class="keyword">do</span><br /><span class="keyword">def </span><span class="method">initialize</span><br /> <span class="attribute">@version</span><span class="punct">=</span><span class="number">2</span><br /><span class="keyword">end</span><br /><span class="keyword">end</span><br /><span class="ident">puts</span> <span class="constant">A</span><span class="punct">.</span><span class="ident">new</span><span class="punct">.</span><span class="ident">version</span><br /></pre><br /><!-- ******************************************************** End Code: Kernel#new_style_class ************************************************************* --><br />Note that the such created <i>new_style_class</i> is a straight forward Ruby class that you can use and monkey patch as any other Ruby class.<br /><br /><span style="font-weight: bold;"><span style="font-size:130%;">Did I Say Classless?</span></span><br /><br />Easy enough, let us just forget about Class.<br /><br />Instead of creating the classic Ruby class and storing it in the class constant, why not just creating objects with a Kernel method, a little bit like in Javascript.<br /><br />Then we extend them with our anonymous modules on the fly.<br /><br />I will finish this post with some code demonstrating the usage of classless Ruby but please note how Ara's brilliant idea is still there, just look into Object#ext_object.<br /><!-- ************************************************************* Begin Code: Classless Ruby *******************************************************************--><br /><pre><span class="constant">PrototypeError</span> <span class="punct">=</span> <span class="constant">Class</span><span class="punct">::</span><span class="ident">new</span><span class="punct">(</span> <span class="constant">StandardError</span> <span class="punct">)</span><br /><span class="keyword">class </span><span class="class">Prototype</span><br /> <span class="ident">attr_accessor</span> <span class="symbol">:__proto__</span><br /> <span class="keyword">class </span><span class="punct"><<</span> <span class="constant">self</span><br /> <span class="ident">alias_method</span> <span class="symbol">:orig_new</span><span class="punct">,</span> <span class="symbol">:new</span><br /> <span class="keyword">def </span><span class="method">new</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">block</span><br /> <span class="ident">o</span><span class="punct">=</span><span class="ident">orig_new</span><span class="punct">(</span> <span class="punct">*</span><span class="ident">args</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">block</span> <span class="punct">)</span><br /> <span class="ident">o</span><span class="punct">.</span><span class="ident">__proto__</span> <span class="punct">=</span> <span class="constant">Module</span><span class="punct">::</span><span class="ident">new</span> <br /> <span class="ident">o</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /><br /> <span class="keyword">def </span><span class="method">deepdup</span><br /> <span class="ident">o</span> <span class="punct">=</span> <span class="ident">dup</span><br /> <span class="ident">instance_variables</span><span class="punct">.</span><span class="ident">each</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">ivar</span><span class="punct">|</span><br /> <span class="ident">val</span> <span class="punct">=</span> <span class="ident">instance_variable_get</span><span class="punct">(</span> <span class="ident">ivar</span> <span class="punct">)</span><br /> <span class="ident">val</span> <span class="punct">=</span> <span class="ident">val</span><span class="punct">.</span><span class="ident">dup</span> <span class="keyword">rescue</span> <span class="ident">val</span><br /> <span class="ident">o</span><span class="punct">.</span><span class="ident">instance_variable_set</span><span class="punct">(</span> <span class="ident">ivar</span><span class="punct">,</span> <span class="ident">val</span> <span class="punct">)</span><br /> <span class="keyword">end</span><br /> <span class="ident">o</span><br /> <span class="keyword">end</span><br /><span class="keyword">end</span> <span class="comment"># class Prototype</span><br /><br /><span class="keyword">module </span><span class="module">Kernel</span><br /> <span class="keyword">def </span><span class="method">new_object</span> <span class="ident">superobject</span><span class="punct">=</span><span class="constant">nil</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">block</span><br /> <span class="keyword">begin</span><br /> <span class="ident">superobject</span><span class="punct">.</span><span class="ident">deepdup</span> <br /> <span class="keyword">rescue</span> <span class="constant">NoMethodError</span><br /> <span class="constant">Prototype</span><span class="punct">.</span><span class="ident">new</span><br /> <span class="keyword">end</span><span class="punct">.</span><span class="ident">ext_object</span><span class="punct">(</span> <span class="punct">&</span><span class="ident">block</span> <span class="punct">)</span><br /> <span class="keyword">end</span><br /><span class="keyword">end</span> <span class="comment"># module Kernel</span><br /><br /><span class="keyword">class </span><span class="class">Object</span><br /> <span class="keyword">def </span><span class="method">ext_object</span> <span class="punct">&</span><span class="ident">block</span><br /> <span class="keyword">raise</span> <span class="constant">PrototypeError</span><span class="punct">,</span> <br /> <span class="punct">"</span><span class="string">Not a prototype</span><span class="punct">"</span> <span class="keyword">unless</span><br /> <span class="ident">respond_to?</span><span class="punct">(</span> <span class="symbol">:__proto__</span><span class="punct">)</span> <span class="punct">&&</span> <span class="constant">Module</span> <span class="punct">===</span> <span class="ident">__proto__</span><br /> <span class="ident">old_proto</span> <span class="punct">=</span> <span class="ident">__proto__</span><br /> <span class="constant">self</span><span class="punct">.</span><span class="ident">__proto__</span> <span class="punct">=</span> <span class="constant">Module</span><span class="punct">::</span><span class="ident">new</span><span class="punct">(</span> <span class="punct">&</span><span class="ident">block</span> <span class="punct">)</span><br /> <span class="ident">__proto__</span><span class="punct">.</span><span class="ident">instance_eval</span><span class="punct">{</span> <span class="ident">include</span><span class="punct">(</span> <span class="ident">old_proto</span> <span class="punct">)</span> <span class="punct">}</span><br /> <span class="ident">extend</span> <span class="ident">__proto__</span><br /> <span class="keyword">end</span><br /><br /> <span class="keyword">def </span><span class="method">clone</span> <span class="punct">&</span><span class="ident">block</span><br /> <span class="ident">new_object</span><span class="punct">(</span> <span class="constant">self</span><span class="punct">,</span> <span class="punct">&</span><span class="ident">block</span> <span class="punct">)</span><br /> <span class="keyword">end</span><br /><span class="keyword">end</span> <span class="comment"># class Object</span><br /><br /><span class="ident">my_object</span> <span class="punct">=</span> <span class="ident">new_object</span> <span class="punct">{</span><br /> <span class="ident">attr_accessor</span> <span class="symbol">:a</span><span class="punct">,</span> <span class="symbol">:b</span><br /> <span class="keyword">def </span><span class="method">to_s</span><span class="punct">;</span> <span class="punct">"</span><span class="string"><span class="expr">#@a</span>, <span class="expr">#@b</span></span><span class="punct">"</span> <span class="keyword">end</span><br /><span class="punct">}</span><br /><span class="ident">my_object</span><span class="punct">.</span><span class="ident">a</span> <span class="punct">=</span> <span class="number">52</span><br /><span class="ident">my_object</span><span class="punct">.</span><span class="ident">b</span> <span class="punct">=</span> <span class="number">60</span><br /><span class="ident">other</span> <span class="punct">=</span> <span class="ident">my_object</span><span class="punct">.</span><span class="ident">clone</span> <span class="punct">{</span><br /> <span class="keyword">def </span><span class="method">reset</span><span class="punct">;</span> <span class="attribute">@a</span><span class="punct">=</span><span class="attribute">@b</span><span class="punct">=</span><span class="constant">nil</span> <span class="keyword">end</span><br /><span class="punct">}</span><br /><span class="ident">puts</span> <span class="ident">my_object</span> <span class="comment"># --> 52, 60</span><br /><span class="ident">puts</span> <span class="ident">other</span> <span class="comment"># --> 52, 60</span><br /><span class="ident">other</span><span class="punct">.</span><span class="ident">reset</span><br /><span class="ident">puts</span> <span class="ident">my_object</span> <span class="comment"># --> 52, 60</span><br /><span class="ident">puts</span> <span class="ident">other</span> <span class="comment"># --> , </span><br /></pre><br /><br /><!-- ******************************************************** End Code: Classless Ruby ************************************************************* -->Robert Doberhttp://www.blogger.com/profile/06304126315464640783noreply@blogger.com0