多くの言語に、コールバックとして作り付けの仕組みがありますが、Rubyの場合は一味違う見方ができるかもしれません。
他言語におけるコールバック
多くの言語に、ある処理から別なコードを呼び出すための仕組みがあります。
C言語
C言語の場合は関数あるいは関数ポインタを使うことになりますが、規格外の手段を使わない限り、動的に関数を生成することはできません。
C++
C++の場合、operator()
を備えたオブジェクトも、関数と同様の構文で呼び出すことができます。そして、関数と関数オブジェクトを統一的に扱う、std::function
のような仕組みもあります。
JavaScript
ホストオブジェクトという例外を除けば、()
を付けて呼び出せるオブジェクトはFunction
オブジェクトです。this
が分離すると意味が変わってしまうことがあるのが要注意かもしれません。
PHP
PHPの場合、コールバックとして'strlen'
のようなグローバル関数名の文字列や、[$object, 'method']
のような配列、そしてClosure
という無名関数のインスタンスを渡せます。渡せるものが多岐にわたるので、is_callable
というチェック専用の関数まであります。
Rubyの文法的な事情
Rubyの場合、上に挙げた4つの言語とは違って、オブジェクトから遊離して呼べる「関数」というものは存在しません。また、operator()
のようなものもなく、関数として呼び出し可能なオブジェクトも作れません。
foo = some_object.some_method(:some_parameter)
# 直接()を付けられるオブジェクトはない
foo(42)
Rubyでコールバックとして使うものとして「ブロック」がありますが、これもメソッドに指定するしかなくて、単体で変数に代入することはできません。
特定のメソッドを呼ぶ形のコールバック
以前Railsのコールバックについて自分が書いた中でも触れましたが、Railsのダックタイピングを活かした「この名前のメソッドを呼ぶ」という形のコールバックもあります。インスタンスでも、クラスやモジュールでも、そのメソッドに応答するなら何でも置くことができます。
Proc
やMethod
ブロックは直接オブジェクト化できませんが、Kernel.#proc
を使うことで、Proc
というクラスのオブジェクトに変換することができます(もっとも、周囲の環境と全く独立なコード片を作りたい場合、Kernel.#lambda
を使ったほうがいいかもしれません)。また、メソッドもMethod
というオブジェクトを抽出可能です。どちらも、ブロックの中身やメソッドを実行したい場合には、.call
というメソッドを使います。
デファクトとしての.call
もちろん、.call
も単なる1メソッドのはずですが、Rubyの世界では特殊な地位があります。
-
foo.call(arg)
を、foo.(arg)
のように略記できます(もっとも、読みづらいのであまりおすすめはしません) - Railsでも使っているRackは、「
.call
を呼べば要素が3つの配列を返す」ものを、Rack middlewareとして扱います。
というように、「何かしら呼び出すもの」を作りたい場合、.call
を呼ぶ形で作っておけば、直接lambda
なども渡せて便利ですので、ダックタイピング的に.call
を見る、というようなハンドリングもいいかなと思いました。