SmalltalkとRubyの動的変更への追従性対決： トレイト vs モジュール

もちろん出来レースです。Ruby には負けてもらいます。ｗ

- [SmalltalkとRubyのオブジェクトの動的性比較とそのからくり](http://qiita.com/sumim/items/2607988633e0df5bb817)
- [Smalltalk・PHPのトレイトとRuby・Scalaのミックスインの違い](http://qiita.com/sumim/items/a53b4f0cd6db353ec0f3)

</br>
Ruby ではモジュールを、それを継承させたいクラスに include することでミックスインできます。

```rb
module M1
  def m1; p "M1#m1" end
end

class C
  include M1
end

C.new.m1   #=> M1#m1
```

オープンクラス機構を有する Ruby は include したモジュールにメソッドを後から追加しても、その変更は即座に反映されます。さすが Ruby 。すごく動的ですね。

```rb
module M1
  def m2; p "M1#m2" end
end

C.new.m2   #=> M1#m2
```

モジュールには別のモジュールもミックスインできます。当然、その結果はすでに当該モジュールを include 済みのクラスやインスタンスにも反映されるはず……ん？

```rb
module M2
  def m3; p "M2#m3" end
end

module M1
  include M2
end

C.new.m3  #=> NoMethodError: undefined method `m3'
```

なのですが、どうやらそうはならないみたいですね。

[2020-03 追記] Rubyで長らく放置されてきたこの“不具合”は、最新の 2.8.0-dev で解消が試みられているようですので、互換性面からの強いクレームや既存機能との整合性等に特に目立った問題がなければ近々是正される…はず^^;

なぜか Ruby のモジュールは、いったん include すると、それに別のモジュールを include してもその結果は既に include 済みのクラス等に対しては反映されない仕様になっている模様。

反映させるためには改めて include してやる必要があるみたいです。

```
class C
  include M1
end

C.new.m1  #=> M1#m1
C.new.m2  #=> M1#m2
C.new.m3  #=> M2#m3
```

</br>
対して Squeak Smalltalk のトレイトも、クラスに use されるのはあくまでその時点でのメソッド群で、その後 use したトレイトに変更があっても追従できるしくみになっているようには見えません。

<blockquote>ClassDescription >> <b>installTraitsFrom:</b><font color="#000000"> </font><font color="#00007F">aTraitComposition</font><font color="#000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#007F7F">&quot;Install the traits from the given composition. This method implements<br>&nbsp;&nbsp;&nbsp;&nbsp;the core composition method - all others are just optimizations for<br>&nbsp;&nbsp;&nbsp;&nbsp;particular cases. Consequently, the optimized versions can always fall<br>&nbsp;&nbsp;&nbsp;&nbsp;back to this method when things get too hairy.&quot;</font><font color="#000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F7F7F">|</font><font color="#000000"> </font><font color="#6B6B6B">allTraits</font><font color="#000000"> </font><font color="#6B6B6B">methods</font><font color="#000000"> </font><font color="#7F7F7F">|</font><font color="#000000"><br><br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">(</font><font color="#7F0000">self</font><font color="#000000"> </font><font color="#00007F">traitComposition</font><font color="#000000"> </font><font color="#00007F">isEmpty</font><font color="#000000"> </font><font color="#00007F">and:</font><font color="#000000"> </font><font color="#007F00">[</font><font color="#00007F">aTraitComposition</font><font color="#000000"> </font><font color="#00007F">isEmpty</font><font color="#007F00">]</font><font color="#000000">)</font><font color="#000000"> </font><font color="#00007F">ifTrue:</font><font color="#000000"> </font><font color="#000000">[</font><font color="#7F0000">^</font><font color="#7F0000">self</font><font color="#000000">]</font><font color="#000000">.</font><font color="#000000"><br><br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#007F7F">&quot;Check for cycles&quot;</font><font color="#000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#6B6B6B">allTraits</font><font color="#000000"> </font><b>:=</b><font color="#000000"> </font><font color="#00007F">aTraitComposition</font><font color="#000000"> </font><font color="#00007F">gather:</font><font color="#000000"> </font><font color="#000000">[</font><font color="#000000">:</font><font color="#00007F">t</font><font color="#000000"> </font><font color="#7F7F7F">|</font><font color="#000000"> </font><font color="#00007F">t</font><font color="#000000"> </font><font color="#00007F">allTraits</font><font color="#000000"> </font><font color="#00007F">copyWith:</font><font color="#000000"> </font><font color="#00007F">t</font><font color="#000000">]</font><font color="#000000">.</font><font color="#000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000">(</font><font color="#6B6B6B">allTraits</font><font color="#000000"> </font><font color="#00007F">includes:</font><font color="#000000"> </font><font color="#7F0000">self</font><font color="#000000">)</font><font color="#000000"> </font><font color="#00007F">ifTrue:</font><font color="#000000">[</font><font color="#7F0000">^</font><font color="#7F0000">self</font><font color="#000000"> </font><font color="#00007F">error:</font><font color="#000000"> </font><font color="#7F007F">'Cyclic trait definition detected'</font><font color="#000000">]</font><font color="#000000">.</font><font color="#000000"><br><br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0000">self</font><font color="#000000"> </font><font color="#00007F">traitComposition:</font><font color="#000000"> </font><font color="#00007F">aTraitComposition</font><font color="#000000">.</font><font color="#000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#6B6B6B">methods</font><font color="#000000"> </font><b>:=</b><font color="#000000"> </font><font color="#7F0000">self</font><font color="#000000"> </font><font color="#00007F">assembleTraitMethodsFrom:</font><font color="#000000"> </font><font color="#00007F">aTraitComposition</font><font color="#000000">.</font><font color="#000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0000">self</font><font color="#000000"> </font><font color="#00007F">installTraitMethodDict:</font><font color="#000000"> </font><font color="#6B6B6B">methods</font><font color="#000000">.</font><font color="#000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#7F0000">self</font><font color="#000000"> </font><font color="#00007F">isMeta</font><font color="#000000"> </font><font color="#00007F">ifFalse:</font><font color="#000000">[</font><font color="#7F0000">self</font><font color="#000000"> </font><font color="#00007F">classSide</font><font color="#000000"> </font><font color="#00007F">updateTraitsFrom:</font><font color="#000000"> </font><font color="#00007F">aTraitComposition</font><font color="#000000">]</font><font color="#000000">.</font><font color="#000000"><br></font></blockquote>

</br>
がしかし、実際に変更を加えてみると…

```smalltalk
| T1 C T2 |
T1 := Trait named: #T1 uses: {} category: 'UserObjects'.
T1 compile: 'm1 ^''T1>>#m1'''.

C := Object newSubclass uses: T1; rename: #C.
C new m1.  "=> 'T1>>#m1 ' "

T1 compile: 'm2 ^''T1>>#m2'''.
C new m2.  "=> 'T1>>#m2' "

T2 := Trait named: #T2 uses: {} category: 'UserObjects'.
T2 compile: 'm3 ^''T2>>#m3'''.
T1 uses: T2.

C new m3.  "=> 'T2>>#m3' "

{C. T1. T2} do: #removeFromSystem "for clean up"
```

と、普通に動的に反映してくれます。勝負ありましたね（出来レース乙）。

もちろん Smalltalk のことですから、泥臭く一生懸命差し替えることで、この動的性を実現していることは言うまでもありません。

![updateTraits.png](https://qiita-image-store.s3.amazonaws.com/0/49275/f32f070a-64c2-69d6-b13c-ce3c0885d1e2.png)

実にけなげですね。