After years of begging for a map function in Cocoa [...]Huh? I rub my eyes, probably just a slip up, but no, he continues:
In a generic language like Swift, “pattern” means there’s a probably a function hiding in there, so let’s pull out the part that doesn’t change and call it map:Not sure what he means with a "generic language", but here's how we would implement a map function in Objective-C.
#import <Foundation/Foundation.h>
typedef id (*mappingfun)( id arg );
static id makeurl( NSString *domain ) {
return [[[NSURL alloc] initWithScheme:@"http" host:domain path:@"/"] autorelease];
}
NSArray *map( NSArray *array, mappingfun theFun )
{
NSMutableArray *result=[NSMutableArray array];
for ( id object in array ) {
id objresult=theFun( object );
if ( objresult ) {
[result addObject:objresult];
}
}
return result;
}
int main(int argc, char *argv[]) {
NSArray *source=@[ @"apple.com", @"objective.st", @"metaobject.com" ];
NSLog(@"%@",map(source, makeurl ));
}
This is less than 7 non-empty lines of code for the mapping function, and took me less than 10 minutes to write in its entirety, including a trip to the kitchen for an extra cookie, recompiling 3 times and looking at the
qsort(3)
manpage
because I just can't remember C function pointer declaration syntax (though it took
me less time than usual, maybe I am learning?). So really, years of "begging" for
something any mildly competent coder could whip up between bathroom breaks or
during a lull in their twitter feed?Or maybe we want a version with blocks instead? Another 2 minutes, because I am a klutz:
#import <Foundation/Foundation.h>
typedef id (^mappingblock)( id arg );
NSArray *map( NSArray *array, mappingblock theBlock )
{
NSMutableArray *result=[NSMutableArray array];
for ( id object in array ) {
id objresult=theBlock( object );
if ( objresult ) {
[result addObject:objresult];
}
}
return result;
}
int main(int argc, char *argv[]) {
NSArray *source=@[ @"apple.com", @"objective.st", @"metaobject.com" ];
NSLog(@"%@",map(source, ^id ( id domain ) {
return [[[NSURL alloc] initWithScheme:@"http" host:domain path:@"/"] autorelease];
}));
}
Of course, we've also had
collect
for a good decade or so, which turns the client code into the following,
much more readable version (Objective-Smalltalk syntax):
NSURL collect URLWithScheme:'http' host:#('objective.st' 'metaobject.com') each path:'/'.
As I wrote in my previous post, we seem to be regressing to a mindset about computer languages that harkens back to the days of BASIC, where everything was baked into the language, and things not baked into the language or provided by the language vendor do not exist.
Rob goes on the write "The mapping could be performed in parallel [..]", for example like parcollect? And then "This is the heart of good functional programming." No. This is the heart of good programming.
Having processed that shock, I fly over a discussion of filter (select) and stumble over the next whopper:
Again...huh?? Our map implementation certainly didn't need (static) types for the list, and all the Smalltalkers and LISPers that have been gleefully using higher order techniques for 40-50 years without static types must also not have gotten the memo.It’s all about the types
We [..] started to think about the power of functions to separate intent from implementation. [..] Soon we’ll explore some more of these transforming functions and see what they can do for us. Until then, stop mutating. Evolve.All modern programming separates intent from implementation. Functions are a fairly limited and primitive way of doing so. Limiting power in this fashion can be useful, but please don't confuse the power of higher order programming with the limitations of functional programming, they are quite distinct.
No comments:
Post a Comment