<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Talk Like A Duck: Tag mixins</title>
    <link>http://talklikeaduck.denhaven2.com/articles/tag/mixins</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>In Ruby, it's not the dog, it's the tricks!</description>
    <item>
      <title>A Subtle Change to Mixin Semantics in Ruby 1.9</title>
      <description>&lt;p&gt;I&amp;#8217;ve been working on a little tool to peek behind the curtain and see a bit of what&amp;#8217;s going on behind the scenes in the standard Ruby implementation (i.e &amp;#8216;ruby&amp;#8217; as opposed to &amp;#8216;Ruby&amp;#8217;)&lt;/p&gt;


	&lt;p&gt;While doing this I was looking at the code which ruby runs when you include a module in a class or another module.  I noticed that ruby 1.8 was going to some pains &lt;b&gt;not&lt;/b&gt; to move the proxy for an included module in the inheritance chain.&lt;/p&gt;


	&lt;p&gt;To verify what my eyes seemed to be telling me, I wrote a silly little test program which created a module with one method, and several classes.&lt;/p&gt;


	&lt;p&gt;This test verified my reading of the 1.8 code.  I then tried the same test using the latest ruby 1.9 and found that module mixin semantics have changed.&lt;/p&gt;


	&lt;p&gt;Read on&lt;/p&gt;


Here&amp;#8217;s my test code:
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;module M1
  def foo
    &amp;quot;foo in M1&amp;quot;
  end
end

module M2
  include M1
end

class C1
  def foo
    &amp;quot;foo in C1&amp;quot;
  end
end

class C2 &amp;lt; C1
  include M1
end

class C3 &amp;lt; C2
  def foo
    &amp;quot;foo in C3&amp;quot;
  end
end

class C4 &amp;lt; C3
  include M1
end

class C5 &amp;lt; C3
  include M2
end

puts &amp;quot;C1 #{C1.new.foo}&amp;quot;
puts &amp;quot;C2 #{C2.new.foo}&amp;quot;
puts &amp;quot;C3 #{C3.new.foo}&amp;quot;
puts &amp;quot;C4 #{C4.new.foo}&amp;quot;
puts &amp;quot;C5 #{C5.new.foo}&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Module mix-in semantics in ruby 1.8&lt;/h2&gt;

And here&amp;#8217;s what we get when we run this using ruby 1.8, with commentary interspersed:
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;rick@frodo:/public/rubyscripts$ ruby include_test.rb
C1 foo in C1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
Note that class C1 defines a method :foo. There should be no surprise that invoking :foo on an instance of C1 gets that definition.
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;C2 foo in M1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
C2 subclasses C1, and also includes module M1, which &lt;b&gt;also&lt;/b&gt; defines a method :foo.  We can see that M1&amp;#8217;definition takes precedence. The search order is, first a singleton class if it exists, then the object&amp;#8217;s class, then any modules included in that class in the reverse order of inclusion, then the superclass, etc.
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;C3 foo in C3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
C3 subclasses C2 and overrides the foo method. Again no surprise.
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;C4 foo in C3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
Here&amp;#8217;s the case which illustrates what I found in the 1.8 code.  C4, a subclass of C3 again includes M1, but instances of C4 resolve foo to the method in C3.  This seems to be odd, since by including M1 in C4, I would expect M1&amp;#8217;s methods to take precedence over those of the superclass.
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;C5 foo in C3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
This final case is a class, C5 which like C4 subclasses C3, and includes a second module M2, which in turn includes M1.  Once again foo resolves to the method defined in C3.
&lt;p&gt;I find the 1.8 implementation counter-intuitive.  As I said it goes to lengths to find an existing inclusion of a module, and re-use it, even if that inclusion was in a superclass.
&lt;h2&gt;Change in ruby 1.9&lt;/h2&gt;
I guess that the core-team agreed with that assessment, or at least the current 1.9 implementation seems to do insertion in a less surprising manner.  Here&amp;#8217;s the result of running the same code with ruby 1.9:
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;$ ruby1.9 include_test.rb
C1 foo in C1
C2 foo in M1
C3 foo in C3
C4 foo in M1
C5 foo in M1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

For easier comparison here&amp;#8217;s the output from ruby 1.8 without commentary.
&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;$ ruby include_test.rb
C1 foo in C1
C2 foo in M1
C3 foo in C3
C4 foo in C3
C5 foo in C3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;I don&amp;#8217;t know about you, but this seems more natural to me.&lt;/p&gt;


	&lt;p&gt;It&amp;#8217;s a subtle change.  I don&amp;#8217;t know how much ruby code re-mixes in modules. I only wrote my test code to verify a quirk I saw in reading the ruby sources.&lt;/p&gt;


	&lt;p&gt;Since the ruby 1.8 implementation made doing so ineffective, I doubt that there is much code which does, but if there is such code, it might break under 1.9, but I think that the change is a good one.&lt;/p&gt;</description>
      <pubDate>Mon, 09 Oct 2006 16:07:00 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:96bd51aa-2993-407b-8ee3-6c6605b14777</guid>
      <author>Rick DeNatale</author>
      <link>http://talklikeaduck.denhaven2.com/articles/2006/10/09/a-subtle-change-to-mixin-semantics-in-ruby-1-9</link>
      <category>ruby</category>
      <category>semantics</category>
      <category>inheritance</category>
      <category>mixins</category>
      <category>changesinruby1.9</category>
      <trackback:ping>http://talklikeaduck.denhaven2.com/articles/trackback/48</trackback:ping>
    </item>
  </channel>
</rss>
