Sub::Rateと言うのを書いた
Sub::Rate - Rate based sub dispatcher generator - metacpan.org
確率に応じて処理を振り分けたいというような要件をカジュアルにクリアできる感じになっております。
たとえばいわゆるガチャ的なものを考えたときに、
my $rate = Sub::Rate->new( max_rate => 100 );
$rate->add( 0.1, sub { say 'Super rare' }); # 0.1 %
$rate->add( 3, sub { say 'Rare' }); # 3 %
$rate->add( default => sub { 'Normal' }); # 残り
my $func = $rate->generate;
みたいな感じで関数を生成できて、この生成した $func
は呼ぶと確率に応じて登録された関数を呼び出してくれる、というようなもの。
母数となる数字(max_rate
)をあらかじめ設定しておいて、そのうちどのくらいの率かというのを関数ごとに指定するインタフェースのモジュールが欲しかったんだけど見つけられなかったので。(検索ワードが悪いという説もある…)
AnyEvent でバックエンドに EV を使う時の注意
AnyEvent を利用する際に注意する必要があることに、コールバック中で発生した例外の処理方法がバックエンドに任されている(=例外処理の方法がバックエンドによってちがう)、というのが挙げられる。
Impl::Perl
では例外は単純に rethrow されるため、プログラム中で例外が発生したり die
したりすると普通にプロセスは終了する。 しかし、Impl::EV
の場合、デフォルトでは例外はキャッチされ標準エラーに出力されるものの、そのまま処理は続行されてしまう。
以下のような1秒タイマーをまわしてタイマーが発火したらアプリを終了する、というようなコードがあったとき、
use strict;
use warnings;
use AnyEvent;
my $cv = AnyEvent->condvar;
my $t; $t = AnyEvent->timer(
after => 1,
cb => sub {
undef $t;
die;
$cv->send;
},
);
print "Backend: ", AnyEvent::detect(), "\n";
$cv->recv;
Impl::Perl
な環境ではこれは期待通り動作するが、Impl::EV
の場合は刺さってしまう。
例外が起きてもそのまま継続する、というのがデフォルト動作なのはどういうわけなのかよくわからないが、手元で Impl::Perl
で開発していて本番で Impl::EV
とかで動かすとこの違いによってはまることはかなりありそう。
この挙動を変えるためには EV の例外処理を上書きするようにすれば良い。 AnyEvent では post_detect
というものでバックエンドモジュールが決まったときのフックを差し込める。それをつかって、
AnyEvent::post_detect {
if ($AnyEvent::MODEL eq 'AnyEvent::Impl::EV') {
no warnings 'once';
$EV::DIED = sub { $cv->croak($@) };
}
};
このようなコードを入れておけばまぁいいんだとおもう。EV のドキュメントにも書かれているが、$EV::DIED
で例外処理を上書きすることはできるのだが、この関数内で発生した例外は無視するそうなので(これどうなのw)、例外を rethrow するというようなことはできない。なのでこのようにどっかに condvar を置いておいてそれの croak
を呼ぶというようなことをする必要がある。
See also:
最近の Mac での EV モジュールの入れ方
Xcode4.2くらい(?) からデフォルトになった clang
だとビルドがこけるので、
$ perl Makefile.PL CC=gcc
などとして gcc
でビルドするようにすれば普通に入ります。
CoffeeScript
なぜ CoffeeScript がダメか - 冬通りに消え行く制服ガールは✖夢物語にリアルを求めない。 - subtech
僕の考えでは JavaScript ネイティブでない(けれど非同期プログラミングやネットワークの知識はある)人が、Node.js を使ってネットワークアプリケーションを書くという用途では結構使えると思う。
デバッグしづらいという点も Node と組み合わせる前提で考えれば coffee
コマンドで直接実行すればいい話である。
また僕自身も小さなネットワークアプリケーションは最近は CoffeeScript で書いてて、fujiwara 氏などにメンテしてもらっているけれど、やはり彼も僕と同じようなスキル(JavaScriptネイティブでない)だから、そういう人が見て大体なんとなく理解できるものになってると感じる。
これがもし、AnyEvent で書いたコードだったらそうはいかなかっただろうなーとw
Cocoa::Skype とか
Cocoa:: なんちゃらでほしいものを聞いていたときにも挙がっていた Cocoa::Skype だけど、その昔Skype4COMを使った記憶があって、SkypeAPI のバインディング書くのはめんどそうな印象だったのだが、 今日 Skype.framework のヘッダファイルみたら、
#import <Cocoa/Cocoa.h>
@protocol SkypeAPIDelegate;
@interface SkypeAPI : NSObject
{
}
+ (BOOL)isSkypeRunning;
+ (BOOL)isSkypeAvailable; // You can only connect and send commands when this method returns YES.
// For example, when Skype is running, but user is logged out, then it returns NO.
+ (void)setSkypeDelegate:(NSObject<SkypeAPIDelegate>*)aDelegate;
+ (NSObject<SkypeAPIDelegate>*)skypeDelegate;
+ (void)removeSkypeDelegate;
+ (void)connect;
+ (void)disconnect;
+ (void)sendSkypeCommand:(NSString*)aCommandString;
@end
// delegate protocol
@protocol SkypeAPIDelegate
- (NSString*)clientApplicationName;
@end
// delegate informal protocol
@interface NSObject (SkypeAPIDelegateInformalProtocol)
- (void)skypeNotificationReceived:(NSString*)aNotificationString;
- (void)skypeAttachResponse:(unsigned)aAttachResponseCode; // 0 - failed, 1 - success
- (void)skypeBecameAvailable:(NSNotification*)aNotification;
- (void)skypeBecameUnavailable:(NSNotification*)aNotification;
@end
と想像以上に短く、これならすぐバインディング書けそうだなーということで気分転換もかねてPerlバインディングをつくってみた。
使い方は付属の example を参照のこと。 Skype.framework の機能は全部つけてある。 Perl サイドのメソッド名とかはまだ変更するかもしれない。暇なときにドキュメントつけてリリースしよう。
libuv の Perl バインディング
気分転換プロジェクトとして、libuvのPerlバインディングを書いている。
現状、timerとtcp周りを一通り実装したところ。
実装は愚直にlibuvの関数とperlの関数を1:1に(uv_tcp_init(...)
をUV::tcp_init(...)
というような形で)マッピングしている。 この実装はlow level APIと位置づけ、その上に使いやすい高レベルなインタフェースをPerl上で用意したいつもり。
このlow level APIをつかったTCP echo serverはこんな感じになる:
use strict;
use warnings;
use UV;
my $server = UV::tcp_init();
UV::tcp_bind($server, '0.0.0.0', 3000)
&& die 'bind error: ', UV::strerror(UV::last_error());
UV::listen($server, 10, sub {
my $client = UV::tcp_init();
UV::accept($server, $client) && die 'accept failed: ', UV::strerror(UV::last_error());
UV::read_start($client, sub {
my ($nread, $buf) = @_;
if ($nread < 0) {
my $err = UV::last_error();
if ($err != UV::EOF) {
warn 'client read error: ', UV::strerror($err);
}
UV::close($client);
}
elsif ($nread == 0) {
# nothing to read
}
else {
UV::write($client, $buf, sub {
my ($status) = @_;
if ($status) {
warn 'client write error: ', UV::strerror(UV::last_error());
UV::close($client);
}
});
}
});
}) && die 'listen error: ', UV::strerror(UV::last_error());
UV::run();
見てわかるようにperlモジュールとしての使いかってはわるいが、libuvを使い慣れた人なら迷いなく使うことが出来るようになっている。 これは僕にとっては発見だった。
また、このサンプルをみてもわかるようにlisten
とかaccept
とかまでラップしている関係上、libuvをAnyEventのバックエンドとしてつかうのは現状は無理そう。
libuv自体はWindowsでも動くはずだが、メインマシンをAirにして以来Windows環境を仮想環境でも持ち歩いていないため確認できていない。 Windows対応してくれる人募集!
開発を始めるためのステップ:
$ cpanm Module::Install
$ cpanm Module::Install::XSUtil
$ git clone git://github.com/typester/p5-UV.git
$ cd p5-UV
$ git submodule update --init
$ perl Makefile.PL
$ make
$ make test
YAPC::Asia 2011
なんだか今年は身内の訃報がかさなったりして、フル参戦できなかったのですが、少しでも行くとモチベーションが上がるのが YAPC ですね。
Hacking Mac OS X Cocoa API from Perl
っていうニッチな発表をしました。補足エントリーは別でかきます。
あと、発表中に似非XSっていう言葉を使ってたんだけど、以外とこの方法で XS 入門するってのはあるなーっておもっているのでいつか(Advent Calendarとか?)まとめたいところ。
感想
見たかったトークの大部分を見ることができず、懇親会にも出られず、だいぶ不完全燃焼ではあるのですが、見ることができた gdb のトークや、組み込みPerlの話、あとたまたま通りがかったmotemenのトークがとてもおもしろかったです!
motemen さんのは普通にメインであってもだいぶおもしろかったとおもうけど、たぶんこれはトークの応募の際の説明とかがだめだったんだろうなと予想!
Lion の /usr/bin/perl とその設定
http://twitter.com/kazuho/status/96099885094412288
@kazuho: /usr/bin/perl は defaults を見て設定されたバージョンの perl を exec するラッパーじゃないかな
ということで、man 引いてみたらそう言うようなことが書いてありました。kazuho++
これはじめてしったんですが、 OSX の perl コマンドは defaults
や環境変数によって実行バージョンやアーキテクチャを変更できるらしい。
$ defaults write com.apple.versioner.perl Version 5.10
とすれば 5.10 がデフォルトになったり、
$ defaults write com.apple.versioner.perl Prefer-32-Bit -bool yes
とかすればさっき意味ないって書いた32bit版のバイナリをつかうようになるみたい。
環境変数でも設定できたり、ユーザーレベルじゃなくシステムレベルでも設定できるようです。詳しくは man perl
してみてください。
Lion の Perl とアーキテクチャ
Snow Leopard の Perl とアーキテクチャ の Lion 版。
インストールされてるPerlは3つ:
/usr/bin/perl
This is perl 5, version 12, subversion 3 (v5.12.3) built for darwin-thread-multi-2level
/usr/bin/perl5.12
This is perl 5, version 12, subversion 3 (v5.12.3) built for darwin-thread-multi-2level
/usr/bin/perl5.10
This is perl, v5.10.1 (*) built for darwin-thread-multi-2level
/usr/bin/perl
と /usr/bin/perl5.12
は同じかと思いきやサイズがだいぶ違う。といっても perl -V
の出力は全く同じでなぞだが…。(i386の方のバイナリが違うのかもしれない)
それぞれユニバーサルバイナリとなっていて、
$ file /usr/bin/perl
/usr/bin/perl: Mach-O universal binary with 2 architectures
/usr/bin/perl (for architecture x86_64): Mach-O 64-bit executable x86_64
/usr/bin/perl (for architecture i386): Mach-O executable i386
$ file /usr/bin/perl5.12
/usr/bin/perl5.12: Mach-O universal binary with 2 architectures
/usr/bin/perl5.12 (for architecture x86_64): Mach-O 64-bit executable x86_64
/usr/bin/perl5.12 (for architecture i386): Mach-O executable i386
$ file /usr/bin/perl5.10
/usr/bin/perl5.10: Mach-O universal binary with 2 architectures
/usr/bin/perl5.10 (for architecture x86_64): Mach-O 64-bit executable x86_64
/usr/bin/perl5.10 (for architecture i386): Mach-O executable i386
という感じで、Snow Leopard の時と違ってアーキテクチャは統一されている。 ppc は完全に切り捨てられてますね。現状 64bit 対応していない CPU では Lion のインストールできないとおもうので i386 バイナリもあまりついている意味はないと思うが…。
また Snow Leopard のときは /usr/bin/perl5.8.9
などとフルバーション表記のバイナリだったが、5.12
とかいうバイナリになっているのでもしかしたらソフトウェアアップデートでバージョンをあげることも考えているのかもしれない。
追記
/usr/bin/perl
について指摘があり追記しました。
Cocoa::EventLoop
そういえば Cocoa::EventLoop と言うモジュールを書いたんでした。
これはもともと AnyEvent::Impl::NSRunLoop として書いていたイベントループ処理部分だけを単体モジュールとして切り出した物で、 AnyEvent を使わなくても Cocoa のイベントループを Perl から使用できるというものです。
くわしくはドキュメントを参照してもらうとして、Cocoa::Growl を使ったサンプルを載せてみます:
use Cocoa::EventLoop;
use Cocoa::Growl;
my $done = 0;
growl_notify(
name => 'Notification Name',
title => 'Hello',
description => 'Cocoa World!',
on_click => sub {
$done++;
},
on_timeout => sub {
$done++;
},
);
Cocoa::EventLoop->run_while(0.1) while !$done;
これを AnyEvent で書くと:
use AnyEvent 5.30;
use Cocoa::EventLoop;
use Cocoa::Growl;
my $cv = AnyEvent->condvar;
growl_notify(
name => 'Notification Name',
title => 'Hello',
description => 'Cocoa World!',
on_click => sub {
$cv->send;
},
on_timeout => sub {
$cv->send;
},
);
$cv->recv;
ほぼ一緒ですね。 また、この例ではわからないですがtimerやioのインタフェースもAnyEventに合わせてあるのでほとんど同じ感じで使うことができます。
ただまぁ、AnyEventには豊富なライブラリがあるため、上記のGrowlのサンプル以上に複雑なことをする場合は素直にAnyEventを使うのがいいかと思います。