LoginSignup
4
0

More than 5 years have passed since last update.

Rubyにおけるcallable

Last updated at Posted at 2018-12-08

多くの言語に、コールバックとして作り付けの仕組みがありますが、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のダックタイピングを活かした「この名前のメソッドを呼ぶ」という形のコールバックもあります。インスタンスでも、クラスやモジュールでも、そのメソッドに応答するなら何でも置くことができます。

ProcMethod

ブロックは直接オブジェクト化できませんが、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を見る、というようなハンドリングもいいかなと思いました。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0