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 なら ( ) で引数渡せる。
よく考えたなぁ。真似しようとは思わないけど。