Underscore.js にインスパイアされた Objective-C のライブラリ Underscore.m を試してみたんだけど、 その使い方にちょっと驚いた。 流れるようなインタフェースで書けるじゃないか。 例えば、公式ページのサンプル。
NSDictionary *dictionary = @{
@"en": @"Hello world!",
@"sv": @"Hej världen!",
@"de": @"Hallo Welt!",
@"ja": [NSNull null]
}
NSArray *capitalized = Underscore.dict(dictionary)
.values
.filter(Underscore.isString)
.map(^NSString *(NSString *string) {
return [string capitalizedString];
})
.unwrap;
Objective-C っぽく無い。 これだけ見ると、JavaScript の Underscore.js を使っていると錯覚してしまいそうだ。
どうやって実装しているか興味が沸いたのでソースコードを見てみた。
一部抜粋。
@interface USArrayWrapper : NSObject ... @property (readonly) USArrayWrapper *(^map)(UnderscoreArrayMapBlock block); @property (readonly) USArrayWrapper *(^filter)(UnderscoreTestBlock block); ... @end @implementation USArrayWrapper ... - (USArrayWrapper *(^)(UnderscoreArrayMapBlock))map { return ^USArrayWrapper *(UnderscoreArrayMapBlock block) { NSMutableArray *result = [NSMutableArray arrayWithCapacity:self.array.count]; for (id obj in self.array) { id mapped = block(obj); if (mapped) { [result addObject:mapped]; } } return [[USArrayWrapper alloc] initWithArray:result]; }; } - (USArrayWrapper *(^)(UnderscoreTestBlock))filter { return ^USArrayWrapper *(UnderscoreTestBlock test) { return self.map(^id (id obj) { return test(obj) ? obj : nil; }); }; } ... @end
なんと、Blocks を返す読み取り専用プロパティだったのか。 確かにプロパティまたは引数無しメソッドなら . で呼び出せるし、 Blocks なら ( ) で引数渡せる。
よく考えたなぁ。真似しようとは思わないけど。