Search code examples
iosswiftfunctional-programmingswift-playground

transducers and swift porting javascript code to swift code


I am trying practice writing transducers in swift, but I cannot successfully translate this functional javascript code to swift.

http://phuu.net/2014/08/31/csp-and-transducers.html

    function mapping(transform) {
        return function (reduce) {
            return function (result, input) {
                return reduce(result, transform(input));
            };
        };
    }


func mapping<T,U> ( transform:(T)-> T ) -> ( (U,T)-> ( (U,T)->U ) ) {

    return { ( transducer:(U,T) ) -> ( (U,T)->U ) in

        return { (initial:U,element:T) -> U in


            var transformedElement = transform(element);

            var  mInitial = transducer(initial,transformedElement); // this line is the problem

            return mInitial;
        }
}




    func addOne (a:Int) -> (Int) {
    return a+1;
}

func concat (c:Array<Int>,element:Int) -> (Array<Int>) {
    var collection = c;

    collection.append(element);

    return collection;

}

var example3 = [1,2,3].reduce([], concat( mapping ( addOne ) ) );

Solution

  • According to http://phuu.net/2014/08/31/csp-and-transducers.html and http://blog.cognitect.com/blog/2014/8/6/transducers-are-coming, a reducing function has the signature

    whatever, input -> whatever
    

    In Swift, this is a function (or closure) of the type

    (U, T) -> U
    

    (using the usual short names T, U for generic types). A transducer is a function that takes one reducing function as an argument and returns another reducing function

    (whatever, input -> whatever) -> (whatever, input -> whatever)
    

    The corresponding Swift type is

    ((U,T) -> U) -> (U,T) -> U
    

    mapping() takes a transformation as an argument an returns a transducer, so it must be defined as

    func mapping<T,U> (transform:(T) -> T) -> ((U,T)->U) -> (U,T) -> U
    

    (Also the parameter in the first inner function of mapping is better called "reducer" instead of "transducer", because it is the argument of the return value.)

    In your example,

    concat : ([Int], Int) -> [Int]
    

    is a reducing function and mapping(addOne) is a transducer, therefore

    mapping(addOne)(concat) : ([Int], Int) -> [Int]
    

    is another reducing function, so that

    var example3 = [1,2,3].reduce([], mapping(addOne)(concat))
    

    gives the result [2,3,4].

    Complete sample code:

    func mapping<T,U> (transform:(T) -> T) -> ((U,T)->U) -> (U,T) -> U {
        return { (reducer:((U,T) -> U)) -> (U,T) -> U in
            return { (initial:U, element:T) -> U in
                return reducer(initial,transform(element))
            }
        }
    }
    func addOne (a:Int) -> (Int) {
        return a+1;
    }
    
    // Slightly simplified and generalized:
    func concat<T> (c:[T], element:T) -> [T] {
        return c + [element];
    }
    
    
    var example3 = [1,2,3].reduce([], mapping (addOne)(concat))
    println(example3) // [2, 3, 4]