Sunday, May 31, 2015

How to Curry Functions in Objective-C

Calling a function is an all-or-nothing ordeal; You either call it or you don't.  There is no in-between.  Or is there?

In functional programming there is a concept of currying, which mean that you can partially apply a function.  If you have 3 arguments, you can partially apply it by using only the first argument.  The function would then return a partial function.  It is only after applying the 2nd and 3rd arguments will you get the a complete return value.

This is not nearly syntactic sugar.  You can pass around this partial function.  It saves on code duplication.  See the below example to see a C function as a curried function.

    typedef NSDictionary *typeA;
    typedef int typeB;
    typedef float typeC;
    typedef NSArray *typeD;
    typedef NSString *typeE;

    typeE(^(^(^day(typeA A))(typeB B))(typeC C))(typeD D) {
        return ^(typeB B) {
            return ^(typeC C) {
                return ^(typeD D) {
                    return (typeE)[NSString stringWithFormat:@"%@ %d %f %@", A, B, C, D];
                };
            };
        };
    }
...
NSLog(@"%@", day(@{})(99)(215.9)(@[@"bye", @"vanishment"]));  // Example usage

    typeE(^(^(^arg1)(typeB))(typeC))(typeD) = day(@{@"å" : @5});
    typeE(^(^arg2)(typeC))(typeD) = day(@{@"å" : @5})(256);
    typeE(^arg3)(typeD) = day(@{@"å" : @5})(256)(3.14);
    typeE arg4 = day(@{@"å" : @5})(256)(3.14)(@[@76]);
    
    NSLog(@"%p", day);
    NSLog(@"%@", arg1);
    NSLog(@"%@", arg2);
    NSLog(@"%@", arg3);

    NSLog(@"%@", arg4);

2015-05-31 11:03:32.694 curry[2566:41971] 0x10a1bfa40
2015-05-31 11:03:32.696 curry[2566:41971] <__NSMallocBlock__: 0x7fa158c1ce60>
2015-05-31 11:03:32.696 curry[2566:41971] <__NSMallocBlock__: 0x7fa158c5e990>
2015-05-31 11:03:32.696 curry[2566:41971] <__NSMallocBlock__: 0x7fa158c13cf0>
2015-05-31 11:03:32.697 curry[2566:41971] {
    "\U00e5" = 5;
} 256 3.140000 (
    76
)

The above is how you can curry a four argument function that returns a value of typeE.

The return type for the day function may look confusing, but its pattern is essentially just repeated over and over again.  Conceivably you could create a macro or work some typedef magic.  Nonetheless, the partial function works by returning blocks.

Here is another example where I use an objective-c method.  I don't recommend it.

    typedef int type1;
    typedef float type2;
    typedef NSArray *type3;
    typedef NSString *type4;

    - (type4(^(^)(type2))(type3))day:(type1)day {
        return ^(type2 deg) {
            return ^(type3 arr) {
                return (type4)[NSString stringWithFormat:@"%d %f %@", day, deg, arr];
            };
        };
    }

Before we end this i'd like to address the issue of type.  Especially if you try to save the partial function into a variable.  You can not cast it to type id.  I mean you could, but you won't be able to call the parameters.  You could however use "typeof()" to get the type.  It does not appear to call the function twice so you are probably safe from calling something twice.