Sequence: a preview
I’m cooking up a utility library for Objective-C called Sequence. It allows you to manipulate sequences of Objective-C objects using functional constructs such as map, reduce, filter, and the like. Check out Sequence.h for the full list of supported operations.
How to use it
Sequence declares category methods on NSObject. When you call one of these methods on a collection object such as NSArray, NSSet, or NSDictionary, those are treated like sequences. If you call one of the methods on a non-collection object such as NSNumber or NSNull, it is treated like a single-element sequence containing that object.
Example: map
NSArray *input = [NSArray arrayWithObjects:@"a", @"b", @"c", nil];
// all sequence-returning methods return a sequence typed as id
id mapped = [input map:^ id (id i) { return [i stringByAppendingString:i]; }];
// to materialize a sequence into an array, send it the -[ array] message.
NSArray *output = [mapped array]; // contains @"aa", @"bb", @"cc"
Example: reduce
NSArray *input = [NSArray arrayWithObjects:@"a", @"b", @"c", nil];
NSString *output = [input reduce:
^ id (id acc, id i) { return [acc stringByAppendingString:i]; } seed:@""];
// output is @"abc"
Example: any
NSArray *input = [NSArray arrayWithObjects:[NSNull null], @"c", [NSNumber numberWithInt:0], nil];
BOOL containsString = [input any:^ BOOL (id i) { return [i isKindOfClass:[NSString class]]; }];
// containsString is YES
How it works
The implementation works with sequences of form (item -> unit) -> unit (called PushSeq) and unit -> (unit -> item) (called PullSeq). PullSeq ends by nil-termination, just like -[NSEnumerator nextObject].
For my first go at it, I made a conscious decision to keep most of the implementation agnostic of which sequence form is used. The consequence of this is that operations like any, all, itemAtIndex, etc. can’t “short-circuit” (return before materializing the whole collection) because their implementation can’t control how many items are enumerated.
Caveats
Operations that could short-circuit currently do not. This is a problem, for example, if you were use this to implement UITableViewDelegate or UITableViewDataSource—you wouldn’t want to have an intermediate array or two being allocated for every call to cellForRowAtIndexPath: when you only want to query a single item out of a sequence.
I’m still thinking about how to best rectify that. My goal is to allow users to define complex declarative structures and query them without allocating intermediate storage beyond the heap objects that make up the query itself.
As a next step, I would like to figure out how to both call and implement NSFastEnumeration. It’s rather obtuse and the docs are terse and highly general, but I’ve a hunch that Sequence could benefit from interacting closely with NSFastEnumeration.