Skip to content

Instantly share code, notes, and snippets.

@baarde
Forked from n-b/fp-obj-c.m
Created February 9, 2015 14:47
Show Gist options
  • Save baarde/21e4ad1bd997f8b2e01d to your computer and use it in GitHub Desktop.
Save baarde/21e4ad1bd997f8b2e01d to your computer and use it in GitHub Desktop.
//#!/usr/bin/env objc-run
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
///
/// Typedefs
typedef id (^TargetMethod)(id arg);
typedef id (^InstanceProperty)(id target);
typedef TargetMethod (^InstanceMethod)(id target);
#define DefineMethod(method, argType, returnType)\
- (returnType (^)(argType)) method;\
+ (returnType (^(^)(id target))(argType)) method;
#define DefineProperty(method, returnType)\
+ (returnType (^)(id target)) method;
InstanceMethod Flip(InstanceMethod method) {
return (InstanceMethod)^(id target) {
return (TargetMethod)^(id arg) {
return method(arg)(target);
};
};
}
///
/// Dispatch
@implementation NSObject (FuncsDefinitions)
+ (BOOL) pfunc_resolveInstanceMethod:(SEL)sel
{
NSString * pfuncName = NSStringFromSelector(sel);
// The methods we’re adding have no arguments in the real Objective-C sense.
if(![pfuncName hasSuffix:@":"]) {
SEL methodSelector = NSSelectorFromString([pfuncName stringByAppendingString:@":"]);
NSMethodSignature * signature = [self instanceMethodSignatureForSelector:methodSelector];
// We’re looking for an existing method of the same name, with 1 argument.
// (We’d need to check the argument and return types.)
if(signature) {
// We’ve found our original method. Let’s add ours:
// First add a method that returns a TargetMethod block.
IMP imp_i = imp_implementationWithBlock(^(id target){
return (TargetMethod)^(id arg) {
return ((id(*)(id,SEL,id))objc_msgSend)(target, methodSelector, arg);
};
});
class_addMethod(self, sel, imp_i, "@?@:");
// And add a class method (that returns an InstanceMethod block, that returns an TargetMethod)
IMP imp_c = imp_implementationWithBlock(^(id targetClass){
return (InstanceMethod)^(id target) {
return (TargetMethod)^(id arg) {
NSAssert(self==targetClass, @"Adding a class method: the receiver should be the class.");
return ((id(*)(id,SEL,id))objc_msgSend)(target, methodSelector, arg);
};
};
});
// `object_getClass` returns our metaclass. Our class methods are our metaclass’s instance methods.
class_addMethod(object_getClass(self), sel, imp_c, "@?@:");
return YES;
}
}
// Call the original implementation
return [self pfunc_resolveInstanceMethod:sel];
}
+ (BOOL) pfunc_resolveClassMethod:(SEL)sel
{
NSString * pfuncName = NSStringFromSelector(sel);
// The methods we’re adding have no arguments in the real Objective-C sense.
if(![pfuncName hasSuffix:@":"]) {
NSMethodSignature * signature = [self instanceMethodSignatureForSelector:sel];
// We’re looking for an existing method of the same name, with 0 argument.
// (We’d need to check the return type.)
if(signature) {
// Add a method that returns a InstanceProperty block.
IMP imp_i = imp_implementationWithBlock(^(id targetClass){
return (InstanceProperty)^(id target) {
return ((id(*)(id,SEL))objc_msgSend)(target, sel);
};
});
class_addMethod(object_getClass(self), sel, imp_i, "@?@:");
return YES;
}
}
// Call the original implementation
return [self pfunc_resolveInstanceMethod:sel];
}
+ (void)load {
method_exchangeImplementations(class_getClassMethod(self, @selector(pfunc_resolveInstanceMethod:)),
class_getClassMethod(self, @selector(resolveInstanceMethod:)));
method_exchangeImplementations(class_getClassMethod(self, @selector(pfunc_resolveClassMethod:)),
class_getClassMethod(self, @selector(resolveClassMethod:)));
}
@end
///
/// Test code
void Print(id<NSObject> obj) {
printf("%s\n",obj.description.UTF8String);
}
@implementation NSArray (Map_)
- (NSArray*) map:(id (^)(id target))block_
{
NSMutableArray * result = [NSMutableArray new];
for (id target in self) {
[result addObject:block_(target)];
}
return result;
}
- (NSArray*) reduce:(id (^(^)(id target))(id arg))block_
{
id result = [self firstObject];
if([self count]<2) {
return result;
}
for (id arg in [self subarrayWithRange:NSMakeRange(1, [self count]-1)]) {
result = block_(result)(arg);
}
return result;
}
@end
@implementation NSString (Plus)
- (NSString*) plus:(NSString*) string
{
return [self stringByAppendingString:string];
}
@end
///
///
@interface NSArray (Funcs)
DefineMethod(map, id (^)(id), NSArray*);
DefineMethod(reduce, id (^(^)(id))(id), NSArray*);
@end
@interface NSString (Funcs)
DefineProperty(uppercaseString, NSString*);
DefineMethod(plus, NSString*, NSString*);
@end
///
///
int main()
{
Print(@[@"foo", @"bar", @"baz"].map(NSString.uppercaseString));
// -> ( FOO, BAR, BAZ )
Print(@"Hello".plus(@" I love you"));
// -> Hello I love you
Print(@[@"foo", @"bar", @"baz"].reduce(NSString.plus));
// -> foobarbaz
InstanceProperty massUppercaser = Flip(NSArray.map)(NSString.uppercaseString);
Print(massUppercaser(@[@"abc",@"def"]));
// -> ( ABC, DEF )
InstanceProperty exclamate = Flip(NSString.plus)(@"!!");
Print(@[@"quick", @" to the batmobile"].map(NSString.uppercaseString).map(exclamate).reduce(NSString.plus));
// -> QUICK!! TO THE BATMOBILE!!
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment