Techioz Blog

MRI: 一部のメソッドはエイリアスとして実装されているのに、他のメソッドは重複しているのはなぜですか?

概要

MRI コードベースを参照すると、エイリアス化されたメソッドの中にはエイリアスとして定義されているものと、そうでないものがあることがわかります。

たとえば、TrueClass#inspect は、その #to_s (ソース) のエイリアスです。

rb_define_alias(rb_cTrueClass, "inspect", "to_s");

…しかし、オブジェクト#は_aですか?は kind_of? のエイリアスではなく、同じ実装関数 (ソース) を持つ 2 つの別個のメソッドを定義します。

rb_define_method(rb_mKernel, "kind_of?", rb_obj_is_kind_of, 1);
rb_define_method(rb_mKernel, "is_a?", rb_obj_is_kind_of, 1);

この違いは、「ユーザー空間」Ruby からも観察できます。

# Aliased methods have a different `original_name`:
true.method(:inspect).original_name # => :to_s

# "Duplicated" methods don't:
true.method(:is_a?).original_name # => :is_a?
true.method(:kind_of?).original_name # => :kind_of?

エイリアスが使用される場合と使用されない場合のパターンはありますか?違いはありますか?

解決策

これは決して質問に対する権威ある答えではなく、提起された質問に真に答えているわけでもありません。ただし、提供されるオプション間にいくつかの特徴があり、合理的なコメントの形式を満たすには冗長すぎます。

質問

言えません。おそらく、Matz またはコア チームの一部だけがこれに答えることができるでしょう。

ここに示すようにそれが可能です。

参照される function1 (またはメソッド) の再定義に関しては、エイリアシングと同じ実装で複数のメソッドを定義することの間には顕著な違いがあります。

エイリアシング

rb_define_alias(rb_cTrueClass, "inspect", "to_s");

rb_define_alias は、rb_method_entry_clone を使用してエイリアスを作成するときに実際に関数定義のクローンを作成します。

関数は実際にクローン化される (参照されるだけではない) ため、その後の rb_mod_to_s (関数 to_s が参照する) への変更や、Ruby コード内で実際の to_s メソッドを再定義しても、inspect の定義には影響せず、元の関数を引き続き表現します。

これは、rb_define_alias と本質的に同じ関数呼び出しを使用して、高レベル Ruby の alias と alias_method2 がどのように機能するかです。

同じ実装を持つ個別のメソッド定義

rb_define_method(rb_mKernel, "kind_of?", rb_obj_is_kind_of, 1);
rb_define_method(rb_mKernel, "is_a?", rb_obj_is_kind_of, 1);

rb_define_method は基になる関数へのメソッド参照を作成するだけなので、rb_obj_is_kind_of の実装が変更されると、そのような変更は両方の kind_of に影響しますか?そしてis_aですか?なぜなら、それらはその関数を直接参照しているからです。

より高いレベルの例

Ruby 内でも同様の動作が見られます。

class Foo 

   # Aliasing
  def to_s = 'Foo'
  alias inspect to_s 
  # or even this which more visibly represents the concept of cloning a method
  define_method(:other_inspect, instance_method(:to_s))

  # Separate Method Definitions with the same implementation
  def _kind_of? = true
  define_method(:is_a?) {_kind_of?}
  define_method(:kind_of?) {_kind_of?}
end 

Foo.new.to_s #=> 'Foo' 
Foo.new.inspect #=> 'Foo' 
Foo.new.other_inspect #=> 'Foo' 
Foo.new.is_a? #=> true
Foo.new.kind_of? #=> true

class Foo 
  def to_s = 'Bar'
  def _kind_of? =false
end 

Foo.new.to_s #=> 'Bar' 
Foo.new.inspect #=> 'Foo' 
Foo.new.other_inspect #=> 'Foo' 
Foo.new.is_a? #=> false 
Foo.new.kind_of? #=> false 

また、to_s の定義が変更されていても、inspect と other_inspect の両方の「元の名前」が依然として to_s に戻ることにも注意してください。

  1. この質問の範囲を超えて、alias と alias_method の間には明確な違いがあります。