AnyEvent::Intro チョー訳 その1

最近perl界隈で話題のAnyEventについて、今のうちに習得しておくとid:miyagawaみたいにクールになれて、もしかしてモテるのではないかと思い、よっしゃ、勉強してみるか、ということでドキュメント読み出したんですが、、えらいボリュームですね。心がポッキリ折れました。

い、いや、そんなことではいかん!頑張るぞ。

というわけで、折角なので、チョー訳を残していこうと思います。AnyEventはおさえておきたいけどドキュメントの長さに呆然としてしまったアナタ、ダウンロードたけしと一緒にレッツスタディしようぜ!


なお、AnyEventのドキュメントはすごく長いので、ぶつ切りでいきます。最後まで書き綴れるかどうかは不明です。

さらに英語にはあまり自信がないくせに、要所要所ですごく意訳的なことをしています。誤訳などありましたら指摘して下さいませ。

※原文はこちら
http://search.cpan.org/~mlehmann/AnyEvent-5.2/lib/AnyEvent/Intro.pod

Introduction to AnyEvent (AnyEventへの導入)

このチュートリアルではAnyEventの特徴を紹介していきます。

まず最初にAnyEventの中核モジュールを紹介します。これにはすでに必要なもの全てが供給されているかもしれません。もしあなたがAnyEventのイベントハンドリング機能だけに興味があるのならば、これ以上読む必要はありません。

次にソケットを使ったネットワークプログラミングに焦点をあてます。AnyEventはあなたが利用できる数多くのサポートと、お粗末なポータビリティに対する数多くの回避方法を提供しています。

what is AnyEvent?(AnyEventとは何か?)

もしもAnyEventについての存在理由には興味がなく、且つすぐにコードを見たいのならば、このセクションは飛ばしてください。

まず第1に、AnyEventはイベントベースなプログラミングをするための単なるフレームワークです。一般的にそのようなフレームワークは「0か1か」の代物です。そのようなフレームワークを使う場合、同一のプログラム内で同様の他のフレームワークを一緒に使うことは、簡単にはできないか、もしくは全く不可能でしょう。

AnyEventは違います。 - DBIが多くの異なるデータベースAPIの抽象概念であるように、AnyEventは他のイベントループの最上位層に位置する、薄い抽象概念です。AnyEventの主たる目的は、イベントループフレームワークに関する選択肢を、モジュール作者の手からモジュールを利用するプログラム作者の手に委譲させることです。

つまり、あなたは同一プログラム内で、あなたが使ったのと同じフレームワークを利用することを他のコードに強要することなく、そのコードがすべきことを制御するために、イベントを使ったコードを書くことが出来るようになります。すなわち、あなたはAnyEventを使ったイベントベースのPerlモジュールを作りつつ、そのモジュールのユーザは、Gtk2、Tk、Event(または内部でirssi やrxvt-unicodeをはしらせたり)もしくは他のどのイベントループをサポートしたものであっても、なおも選択することが出来ます。さらにAnyEventは、pure-perlで実装されたイベントループと合わせて提供されるので、あなたのコードは、他のイベントループモジュールのインストールの可否に関わらず動きます。後者は重要です。AnyEventは他のモジュールへの強い依存性がないので、例えばCコンパイラーがなかったとしても、インストールを容易にします。どんな環境であっても、AnyEventはうまく対処するでしょう。

Net::IRCのような既存perlモジュールの典型的な制約は、それらが独自のイベントループで構成されていることです。Net::IRCにおいて、それを使うプログラムはNet::IRCのイベントループをスタートさせる必要があります。それはつまり、そのモジュールもまた独自のイベントループ(すなわちGlib)を使うことを強要するため、例えばこのモジュールをGtk2のGUIにインテグレートさせることは出来ないということを意味します。

もう1つ例をあげるとLWP です。それは純粋なブロッキングを伴うHTTP(FTPやそれ以外も可能な)クライアントライブラリで、もしもHTTPリクエストが返ってくるのを待っている間に他の何かの処理を行いたいのならば、たいてい、他のプロセスをスタートさせるかHTTPのために別プロセスをフォークさせるか、もしくは(Coro::LWPのような) スレッドを使う必要があるでしょう。

これらの設計の背景において、しばしば、モジュールが複雑なXSモジュール(Net::IRCのような)に依存したくないことや、もしくはユーザに特定のエベントループの利用を強制したくないということ、モジュールの有用性をサーバ的に制限してしまう恐れから逃れたいということがモチベーションとなります。もしもあなたの書いたモジュールがGlibを必要とするならば、それはTkのプログラムでは動かないのです。

AnyEventはこのジレンマを、モジュール作者に以下の2つのことをさせないことにより、解決します。

  • それ自身の独自のイベントループを書くこと(どこであってもイベントループの可用性を保証するために。たとえ特殊なモジュールがインストールされていないwindowsであっても)
  • 1つの特定のイベントループを選ぶこと(Perlで利用できるほとんどのイベントループでAnyEventは動くため。)

もしもモジュール作者が彼の(彼女の)必要とするすべてのイベント処理(IOイベント、タイマー、シグナル...)のためにAnyEventを使うのならば、他のすべてのモジュールは彼のモジュールを簡単に利用できるし、彼の利用するイベントループを選択する必要も、それに適応させる必要もありません。イベントループの選択は最終的にプログラム作者(すべてのモジュールを使いメインプログラムを書く人)によってなされます。そして、それでもなお、彼はイベントループを選択する必要はありません。システムで利用可能な最適なイベントループを、AnyEventに選ばせることができます。

これに関してのより具体的な事柄はAnyEventモジュールのメインドキュメントを読んで下さい。

Introduction to Event-Based Programming(イベントベースプログラミングへの導入)

さて、イベントをつかったプログラミングとは正確にはどのようなものでしょう?それはごくシンプルに言えば、例えばSTDINからユーザが何かを入力するような、積極的に何かを待っているようなコード、に取って代わるものを意味します。

$| = 1;  print "enter your name> ";
my $name = <STDIN>;

上記の替わりに、コールバックカニズムを使って、イベントフレームワークに対して、STDINで何かしらのデータが利用可能となったらあなたに通知するように命じてみます。

use AnyEvent; 

$| = 1; print "enter your name> "; 

my $name; 

my $wait_for_input = AnyEvent->io ( 
	fh => \*STDIN, # which file handle to check 
	poll => "r", # which event to wait for ("r"ead data) 
	cb => sub { # what callback to execute 
		$name = <STDIN>; # read it 
	} 
); 

# do something else here

より複雑なように見えるし、きっとそうなんでしょう。しかし、イベントを使う利点は、プログラムが入力を待っている替わりに他の何かを処理できる、という点にあります(傍注: AnyEventをCoroのようなスレッドパッケージと合わせて使うことにより、多くの単純さを取り戻すことができ、事実上、両方のいいとこ取りが出来るようになります)。

最初の例であったようなwaiting(待機)は、プロセスを"ブロッキング"しているとも呼ばれます。なぜならばあなたがそうしている間にも、そのプロセスが他の何かを実行することを"ブロック"しているからです。

2番目の例では、イベント読み込みだけに関心を注ぐことにより、ブロッキングを回避しています。そしてそれは高速でかつプロセスをブロックしません。データが読み込み可能となった時だけ、コールバックが呼ばれます。そして、その後データを読むことが可能となります。

”関心"とは"ウオッチャー"と呼ばれるAnyEvent->ioによって返されるオブジェクトにより表現されます。 - それはあなたの関心のあるイベントのために、ファイルハンドル(もしくは他のイベントソース)を"ウォッチ”するのでそのように呼ばれています。

上記の例では、私たちはAnyEvent->ioメソッドを呼ぶことによりI/Oウォッチャーを作っています。あるイベントにおいて、関心を無効化するということは、シンプルにウォッチャーについて忘れるということ、例えばストアされた変数をundefすることにより表現されます。AnyEventはウォッチャーがすでに使われていないのであれば、自動的にクリーンアップしてくれるでしょう。あたかも、Perlがもうどこでも使わないファイルハンドルをクローズするのと同じように。

  • short note on callbacks(コールバックに関するショートノート)
    • 人々に打撃を与える一般的な問題は、コールバックにわたすパラメータの問題です。Cや C++のような言語を使うプログラマーは、しばしばファンクションのアドレス(リファレンス)とデータを渡すようなスタイルに慣れています。例えば
sub callback { 
	my ($arg) = @_; 

	$arg->method;
}

my $arg = ...; 

call_me_back_later \&callback, $arg;
    • これはイケテません。振る舞いが定義される(コールバックが登録される)場所は、しばしば振る舞いが実装される場所からは遠く離れていることがあるからです。またコードを呼び出すのにPerlシンタックスをも使っていません。またコールバックに名前をつけなければならないという抽象的なペナルティも存在します。これはしばしば不必要だし、無意味になるか名前を複製するようになります。
    • Perlでは、クロージャをつかってより直接的に振る舞いを定義できます。クロージャとは、それが生成される際に、囲われたスコープ内にリファレンスが渡されるようなコードブロックのことです。これはクロージャが生成された時点でのレキシカル変数が、そのクロージャの中で単に使える、ということを意味しています。
my $arg = ...;

call_me_back_later sub { $arg->method };
    • ほとんどの状況下において、クロージャはより高速で、より少ないリソースを消費し、伝統的なアプローチとなるより明快なコードをもたらします。より高速とは、Perlにおいてはローカル変数にパラメータを渡したり保管したりするということは相対的に遅いためです。より少ない資源とは、クロージャは、すでに存在している変数をあらたに作ることなくリファレンスを得るためです。そしてより明快なコードとは、2番目の例において明白なように、コールバックが実行される時点で、速やかにメソッドが呼び出されています。
    • これらのことは別としても、AnyEventで使うクロージャの最強の引数は、AnyEventはコールバックに対してパラメータを許していないということです。すなわち、ほとんどのケースにおいて、クロージャだけがそれを達成する唯一の方法となります。
  • A hint on debugging(デバッグに関するヒント)
    • AnyEventはデフォルトでは引数のチェックはなにもしません。特にAnyEventを使って、あなたなりのやり方を学ぼうとしているのなら、このことは奇妙かつ期待していない結果をもたらすこともあり得ます。
    • AnyEventは特別な"strict“モード(デフォルトでオフ)をサポートしています。それはかなり厳しい引数のチェックですので、その代償として幾分遅くなります。しかしながら、開発工程においてはこのモードはとても役に立ちます。環境変数PERL_ANYEVENT_STRICTをTRUEにすることでこのstrictモードを有効にすることができます。
PERL_ANYEVENT_STRICT=1 perl test.pl
    • もしくはプログラムの中で use AnyEvent::Strict と書くことで、同じ効果を得ることもできます。(ただし製品版ではこうしてはなりません)