Benjamin van der Veen

I live in Portland, Oregon. I work as a user experience designer at Emma.

Take a look at my portfolio, follow me on Twitter @bvanderveen, or email me at .

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.


Copyright © 2012 Benjamin van der Veen. atom feed