昔々、C++がcfrontというトランスレータによって実装されていたころ; objがクラスFooのオブジェクトであるとき、メソッド呼び出し obj.bar(arg) は、だいたい*1 Foo_bar(ptr_to_obj, arg) のような関数呼び出しに翻訳されていました。つまり、メソッド呼び出しは、特殊な形式の関数呼び出しと解釈されていたわけです。しかし一方、関数は特殊なメソッドであるという解釈も可能です。
「関数が基本的な存在か、それともメソッドがより基本的か?」-- この問に確たる答があるわけじゃなくて、「別にどっちでもいいが、どっちかに決めるのだ」ということでしょう。以下、特定のプログラミング言語によらない大ざっぱな記法を使って述べます。
「foo(obj, arg) ←→ obj.foo(arg)」という相互変換の関係があったとします。関数を基本にしたいなら、obj.foo(arg) を foo(obj, arg) と思って一件落着。関数をメソッドに帰着したいなら、グローバルオブジェクトglobalを考えて、関数呼び出し foo(arg) は global.foo(arg) の略記と思えばこれも一見落着。
関数をグローバルオブジェクトのメソッドではなくて、それ自体がオブジェクトだと思いたいときもあります。関数fooが、実はオブジェクトであるとすると、関数呼び出し foo(arg1, arg2) は、foo.call(arg1, arg2) のようなメソッド呼び出しと考えることになるでしょう(callは可変引数だとします)。
「foo(obj, arg) ←→ obj.foo(arg)」と「foo(arg1, arg2) ←→ foo.call(arg1, arg2)」の両方を一度に仮定すると*2何が起きるでしょう。以下の3つの呼び出し形式が同値になります。
- obj.foo(arg) … メソッド呼び出し
- foo(obj, arg) … 関数呼び出し
- foo.call(obj, arg) … callメソッド呼び出し
最後のcallメソッド呼び出しも、メソッド呼び出しの一種ですから、以下の3つも同値ですよね。
- foo.call(obj, arg) … メソッド呼び出し
- call(foo, obj, arg) … 関数呼び出し
- call.call(foo, obj, arg) … callメソッド呼び出し
同様に、次の3つも同値です。
- call.call(foo, obj, arg) … メソッド呼び出し
- call(call, foo, obj, arg) … 関数呼び出し
- call.call(call, foo, obj, arg) … callメソッド呼び出し
ヒエエエー、キリがないよー。
このまま続けると無限に展開できるので、人為的にどっかでケリをつけることになります。例えば、関数オブジェクトのメソッドcallは、実装内部にあって外からは見えない関数 _CALL に展開されてオシマイだとすれば:
- obj.foo(arg) … メソッド呼び出し
- foo(obj, arg) … 関数呼び出し
- foo.call(obj, arg) … callメソッド呼び出し
- _CALL(foo, obj, arg) … オシマイ!
ハーッ、ほっとした。
*1:正確なところは憶えてません。
*2:追記:JavaScriptでは、この仮定に近い状況になると思います。