Skip to content

Instantly share code, notes, and snippets.

@landonf
Created January 6, 2015 17:12
Show Gist options
  • Save landonf/3e04ea3c7b13f68ab746 to your computer and use it in GitHub Desktop.
Save landonf/3e04ea3c7b13f68ab746 to your computer and use it in GitHub Desktop.
consequentialism was an experiment in improving ObjC with C++ templates
@interface SeqTests : XCTestCase @end
@implementation SeqTests
/* Construction examples */
- (void) testConstruction {
}
/* Some example uses */
- (void) testExample {
/* A simple example of typesafe map/filter/flatten/foreach operations. Each step is type-checked based on inferring
* the return type of the previous step; unlike a pure ObjC solution, the compiler will raise an error if you
* return or provide a block that operates on the wrong type. */
{
/* Filter out locked campains, extract their venues, flatten them into a single sequence, and log their regions. */
self.campaigns.filter(^(PPCampaign *campaign) {
return campaign.locked;
}).map(^(PPCampaign *campaign) {
return campaign.venues;
}).flatten().foreach(^(PPVenue *venue) {
NSLog(@"This entire process was fully type-checked, with inferred types! %@", venue.region);
});
}
/* If we explicitly declare our types (rather than relying on type inference), the above can be written in long-form
* as: */
{
/* Fetch all campaigns */
Seq<PPCampaign *> campaigns = self.campaigns;
/* Filter out all unlocked campaigns */
Seq<PPCampaign *> lockedCampaigns = campaigns.filter(^BOOL (PPCampaign *item) {
return item.locked;
});
/* Fetch the list of venues from each campaign */
Seq<Seq<PPVenue*>> allVenues = lockedCampaigns.map(^Seq<PPVenue*> (PPCampaign *campaign) {
return campaign.venues;
});
/* Flatten the list of venues */
Seq<PPVenue*> venues = allVenues.flatten();
/* Log the venue's regions */
venues.foreach(^void (PPVenue *venue) {
NSLog(@"This was a much more verbose way to do the exact same thing: %@", venue.region);
});
}
/* Lastly, if we were to do this in pure ObjC, this would be the equivalent.
*
* The code is nearly as short as the first one, but:
* - The types returned from the Objective-C container classes are `id', and are not checked by the compiler.
* - We're forced to use mutable types to gather the results.
* - The stages are not easily composed / re-composed.
*
* NOTE: The toObjC() calls are an artifact of our model objects returning Seq<T> types, which require an explicit
* call to acquire access to the backing plain ObjC representation.
*/
{
/* Extract the venues from all locked campaigns. */
NSMutableArray *venues = [[NSMutableArray alloc] init];
for (PPCampaign *campaign in self.campaigns.toObjC()) {
if (!campaign.locked) {
continue;
}
for (PPVenue *venue in campaign.venues.toObjC()) {
[venues addObject: venue];
}
}
/* Log the venue's regions */
for (PPVenue *venue in venues) {
NSLog(@"This the ObjC way: %@", venue.region);
}
}
}
- (Seq<PPCampaign*>) campaigns {
Seq<NSString*>(@"String", @"String");
return Seq<PPCampaign*>(
[[PPCampaign alloc] initWithVenues: Seq<PPVenue*>(
[[PPVenue alloc] initWithRegion: @"North America"],
[[PPVenue alloc] initWithRegion: @"South America"]
) locked: NO],
[[PPCampaign alloc] initWithVenues: Seq<PPVenue*>(
[[PPVenue alloc] initWithRegion: @"Europe"],
[[PPVenue alloc] initWithRegion: @"Happy Fun Zone"]
) locked: YES]
);
}
@end
@implementation PPVenue
- (instancetype) initWithRegion: (NSString *) region {
if ((self = [super init]) == nil) return nil;
_region = region;
return self;
}
@end
@implementation PPCampaign
- (instancetype) initWithVenues: (Seq<PPVenue*>) venues locked: (BOOL) locked {
if ((self = [super init]) == nil) return nil;
_venues = venues;
_locked = locked;
return self;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment