Ruby と Smalltalk のリフレクション機能対応表

引き続き、日経 Linux 10 月号、「プログラミングのオキテ」にある Ruby のリフレクション機能の表を参考に、Squeak システムの Smalltalk との対応表を作ってみました。

リフレクション機能 Ruby SqueakSmalltalk
メソッド名の一覧 Foo.instance_methods Foo allSelectors
メソッド名の一覧
(継承分を除く)
Foo.instance_methods(false) Foo selectors
オブジェクトのメソッド名の一覧 obj.methods obj class allSelectors
特異メソッド名の一覧 obj.singleton_methods " n/a "
特異メソッド名の一覧
(継承分を除く)
obj.singleton_methods(false) " n/a "
インスタンス変数名の一覧 obj.instance_variables Foo allInstVarNames
インスタンス変数名の一覧
(継承分を除く)
#n/a Foo instVarNames
インスタンス変数名の一覧
(継承関係にあるもの全て)
#n/a Foo allInstVarNamesEverywhere
サブクラスのインスタンス変数名の一覧 #n/a Foo subclassInstVarNames
オブジェクトのインスタンス変数名の一覧 obj.instance_variables obj class allInstVarNames
インスタンス変数値の取得 obj.instance_variable_get(:@var) obj instVarNamed: #var
インスタンス変数値の設定 obj.instance_variable_set(:@var, "value") obj instVarNamed: #var put: 'value'
インスタンス変数の追加 obj.instance_variable_set(:@var, "value") Foo addInstVarName: #var
インスタンス変数の削除 obj.instance_eval{remove_instance_variable(:@var)}
obj.__send__(:remove_instance_variable,:@var)
Foo removeInstVarName: #var
インスタンス変数の定義クラス #n/a Foo classThatDefinesInstanceVariable: #var
非参照のインスタンス変数名の一覧 #n/a Foo allUnreferencedInstanceVariables
非参照のインスタンス変数名の一覧
(継承分を除く)
#n/a Foo unreferencedInstanceVariables
グローバル変数名の一覧 global_variables Smalltalk keys
ローカル変数名の一覧 local_variables thisContext tempNames
クラス(モジュール)定数名の一覧 Foo.constants Smalltalk associations select: [:assoc|
   assoc isKindOf: ReadOnlyVariableBinding]
定数値の取得 Foo.const_get(:FOO) Smalltalk at: #foo
定数値の設定 Foo.const_set(:FOO, "value") (Smalltalk associationAt: #foo)
   privateSetKey: #foo value: 'value'
定数の削除 Foo.class_eval{remove_const(:FOO)}
Foo.__send__(:remove_const,:FOO)
Smalltalk removeAt: #foo
クラス変数名の一覧 Foo.class_variables Foo allClassVarNames
クラス変数名の一覧
(継承分を除く)
#n/a Foo classVarNames
クラス変数値の取得 Foo.class_eval{class_variable_get(:@@var)}
Foo.__send__(:class_variable_get,:@@var)
Foo classPool at: #Var
クラス変数値の設定 Foo.class_eval{class_variable_set(:@@var,"value")}
Foo.__send__(:class_variable_set,:@@var,"value")
Foo classPool at: #Var put: 'value'
クラス変数の追加 Foo.class_eval{class_variable_set(:@@var,"value")}
Foo.__send__(:class_variable_set,:@@var,"value")
}
Foo addClassVarName: #Var
クラス変数の削除 Foo.class_eval{remove_class_variable(:@@var)}
Foo.__send__(:remove_class_variable,:@@var)
Foo removeClassVarName: #Var
クラス変数の定義クラス #n/a Foo classThatDefinesClassVariable: #Var
非参照のクラス変数名の一覧 #n/a Foo allUnreferencedClassVariables
プール変数辞書の一覧 #n/a Foo allSharedPools
プール変数辞書の一覧
(継承分を除く)
#n/a Foo sharedPools
プール変数辞書の追加 #n/a Foo addSharedPool: #VarDict
プール変数辞書の削除 #n/a Foo removeSharedPool: #VarDict
メソッドを定義 Foo.class_eval{define_method(:foo){"foo"}}
Foo.__send__(:define_method,:foo,proc{"foo"})
Foo compile: 'foo ^#foo'
    classified: ClassOrganizer default
メソッドを削除 Foo.class_eval{remove_method(:foo)}
Foo.__send__(:remove_method,:foo)
Foo removeSelector: #foo
メソッドを未定義化 Foo.class_eval{undef_method(:foo)}
Foo.__send__(:undef_method,:foo)
" n/a "
メソッドに別名を付与 Foo.class_eval{alias_method(:bar,:foo)}
Foo.__send__(:alias_method,:bar,:foo)
"別名のメソッドを定義"
指定メソッドの定義場所の一覧 #n/a SystemNavigation default allImplementorsOf: #foo
指定メソッドの定義場所の一覧
(指定した継承ツリー内限定)
#n/a SystemNavigation default allImplementorsOf: #foo localTo: Foo
指定メソッドのコール場所の一覧 #n/a SystemNavigation default allCallsOn: #foo
指定メソッドのコール場所の一覧
(指定した継承ツリー内限定)
#n/a SystemNavigation default allCallsOn: #foo from: Foo
メソッドカテゴリとメソッド名の一覧 #n/a Foo organization
メソッドカテゴリ内のメソッド名の一覧 #n/a Foo allMethodsInCategory: 'accessing'
メソッドが属するカテゴリの取得 #n/a Foo whichCategoryIncludesSelector: #foo
メソッドカテゴリとメソッドの削除 #n/a Foo removeCategory: 'as yet unclassified'
コンパイル済み)メソッドの取得 Foo.new.method(:foo) Foo >> #foo
メソッドの定義者とその日時 #n/a (Foo >> #foo) timeStamp
メソッドの最初のコメント #n/a Foo firstCommentAt: #foo
メソッドのセレクタ Foo.new.method(:foo).inspect (Foo >> #foo) selector
メソッドの引数の数 Foo.new.method(:foo).arity (Foo >> #foo) numArgs
メソッド定義内のローカル変数の数 #n/a (Foo >> #foo) numTemps
メソッド定義内のリテラルの一覧 #n/a (Foo >> #foo) literals
メソッド定義内のセレクタの一覧 #n/a (Foo >> #foo) messages
メソッド定義のソース #n/a (Foo >> #foo) getSourceFromFile
メソッド定義のソース(デコンパイル #n/a (Foo >> #foo) decompileString
簡易注釈付き
バイトコード出力
#n/a (Foo >> #foo) symbolic
モジュールをインクルード Foo.class_eval{include(Math)}
Foo.__send__(:include,Math)
" n/a "
インクルードされている
モジュールを取得
Foo.included_modules " n/a "
スーパークラスを取得 Foo.superclass Foo superclass
スーパークラスを設定 #n/a Foo superclass: NewSuperOrNil
スーパークラス
(モジュール)名の一覧
Foo.ancestors Foo allSuperclasses
サブクラスの一覧 #n/a Foo subclasses
サブクラスの一覧(孫も) #n/a Foo allSubclasses
クラスカテゴリの取得 #n/a Foo category
クラスカテゴリの設定 #n/a Foo category: 'Category-Name'
クラスコメントの取得 #n/a Foo comment
クラスコメントの設定 #n/a Foo comment: 'I am a sacrifice to you.'
インスタンスの一覧 a=[];ObjectSpace.each_object(Foo){
|obj|a<<obj if obj.class==Foo};a
Foo allInstances
インスタンスの総数 a=[];ObjectSpace.each_object(Foo){
|obj|a<<obj if obj.class==Foo};a.length
Foo instanceCount
インスタンスの一覧
(サブクラス込み)
a=[];ObjectSpace.each_object(Foo){
|obj|a<<obj};a
Foo allSubInstances
インスタンスの総数(サブクラス込み) ObjectSpace.each_object(Foo){} Foo allSubInstances size
継承をフック inherited を定義 #subclass:instanceVariableNames:
classVariableNames:poolDictionaries:
category: をメタクラスに再定義
インクルードをフック included を定義 " n/a "
メソッドの定義をフック method_added を定義 #compile:classified:withStamp:
notifying:logSource: をメタクラスに再定義
メソッドの削除をフック method_removed を定義 #basicRemoveSelector: をメタクラスに再定義
特異メソッド定義をフック singleton_method_added を定義 " n/a "
特異メソッドの削除をフック singleton_method_removed を定義 " n/a "
特異メソッドの未定義化をフック singleton_method_undefined を定義 " n/a "
未定義メソッドの起動をフック method_missing を再定義 #doesNotUnderstand: を再定義
文字列を評価 eval("3+4") Compiler evaluate: '3+4'
オブジェクトの文脈で
文字列を評価
3.instance_eval("self+4") Compiler evaluate: 'self+4' for: 3 logged: false
クラスの文脈で
文字列を評価
Foo.class_eval("name") Compiler evaluate: 'self name' for: Foo logged: false
式が「定義されている」かどうかを判定 defined? " n/a "

定数

Smalltalk では、変更の必要のない定数は通常、メソッドとして定義します。が、最近の Squeak システムでは、グローバル変数を拡張した定数っぽい機能(参照専用のグローバル変数)も使えるようになっています。ただ、Ruby の定数と異なり再代入はできません(警告ではなくエラーになります)。
Smalltalk at: #foo put: 10   " グローバル変数の定義 "
foo   " => 10 "
foo := 20
foo   " => 20 "
(Smalltalk associationAt: #foo) beReadOnlyBinding   " グローバル変数を定数に "
foo := 30   " => Error: Cannot store into read-only bindings "

特異メソッド

Smalltalk には Ruby の特異メソッド(インスタンス特異的メソッド)のようなものはないのですが、Squeak システムには #become: (id:sumim:20050823:p2) を利用した #assureUniClass というメソッドがあるので、これを用いることで似たようなことは可能です。
| ordinary sole |
ordinary := 'string'.
sole := ordinary copy.

sole assureUniClass.   " 特異クラス様クラスを動的に定義してそのインスタンスに #become: "
sole class compile: 'value ^''instance specific...''' classified: 'evaluating'.

^ {{sole reverse. ordinary reverse}. {sole value. ordinary value}}
=> #(#('gnirts' 'gnirts') #('instance specific...' 'string'))
Object >> #value はレシーバをそのまま返します。 コメントにもあるように、Object >> #assureUniClass は、レシーバのクラスの名前に連番を振ったクラスを動的に作成したうえで、さらにそのインスタンスを(レシーバに似せた状態で)生成してレシーバに #become: します。ここで動的に作成されるクラスへの働きかけのほとんどをスーパークラス(#assureUniClass する前のクラス)に委譲してしまうような仕組みを設けることができれば、Ruby の特異クラスとほぼ同様のカラクリ(ひいては特異メソッド)も実現可能でしょう。