June 8, 2014

Swift, Extensions, Single Value Tuples, Passing Closures, and Functional Programming

Posted in iPhone development, mac development tagged , , , , , , , , , , at 2:36 am by tetontech

Swift allows us to extend existing types. This is one of the features of Objective-C that I really like. Because existing types can be modified, I can add methods, and computed properties in the case of Swift, to instances of entities that have been created in a library other than my own. This would not be directly possible using the inheritance model since the library’s provider would not be able to instantiate objects of my sub-class.

That is a lot of verbiage. Let’s look at a specific case. Let’s say a JSON library returns me an Array and I would like that Array to have a new method. Using the inheritance model I would have to create a new instance of an Array subclass and then ensure all the elements of the initial array became part of the new instance. Not the fastest of options. A faster option would be to not sub-class Array but have the new instance of a class contain the Array. This would require creating methods of the new class to duplicate any needed Array methods. Also a non-optimal solution.

Swift’s extensions are a way to resolve this type of issue.  Below is a code example that adds methods to Swift’s Array class, and therefore all instances of Array regardless of their origin.

In the example I add two functional programming methods to Array. They are fold-left and fold-right. These are generalizations of Swift’s standard reduce method. Reduce always returns the same type as is found in the array. Foldl and foldr remove that limitation.

By including a file containing the following code in my swift application these two new functions become available for use on all Arrays.

import Foundation
extension Array{
	//U may or may not be the same type as the array elementsfunc foldl<U>(initial:U, combine:(U,T) -> U) -> U{

		if self.count > 0{

			var combined = initial

			let inverted = self.reverse()

			for index in 0..inverted.count{

				var element = inverted[index]

				combined = combine(combined, element)

			}

			return combined

		}

		return initial

	}
	//U may or may not be the same type as the array elementsfunc foldr<U>(initial:U, combine:(U,T) -> U) -> U{

		if self.count > 0{

			var combined = initial

			for element in self{

				combined = combine(combined, element)

			}

			return combined

		}

		return initial

	}

}

 

Some points to note. Both methods have footprints similar to Swift’s reduce method. This makes it easier to use and remember all three.

func foldl<U>(initial:U, combine:(U,T) -> U) -> U

Fold-left, foldl, is a generic function that uses two generic types;

  1. T – the type of the elements in the Array, and
  2. U – the type of the initial value with which the Array’s elements will be combined.

Foldl also has two parameters. The first is the initial element of type T. The second is a closure bound to the parameter name ‘combine’. This closure also has two parameters. These are found in a Tuple containing values of type U and of type T. The closure returns a value of type U. I have chosen to omit the parenthesis, (), around the U return type declaration since single value Tuples in Swift, such as (U), are compiled to values of the contained type, U, instead of Tuples. I have made this same decision regarding the return type declaration for foldl itself. I think it reduces the visual clutter.

For foldl and foldr to truly be functional methods they must not modify any value outside of themselves since this would violate functional programming’s no side effects rule. The ‘combine’ closure will enforce this rule since it will be unable to modify the Array using the keyword ‘self’. Self, if used in the closure, will not mean the Array being folded. In the example below these methods are called. In these closures ‘self’ would be a UIViewController since self would be captured from the closures environment.

override func viewDidLoad() {
        super.viewDidLoad()
        let b = [5,4,3,2,1]
        
        var result = b.foldl("Folding:", combine: {
            var aString = "\($0) \($1)"
            return aString
            })
        println(result)
        
        
        result = b.foldr("Folding:", combine: {
            var aString = "\($0) \($1)"
            return aString
            })
        println(result)
    }

Run results:

Folding: 1 2 3 4 5

Folding: 5 4 3 2 1

This is not generally the way you would want to build strings from Arrays in all conditions. Foldl and foldr do, however, make it possible to control the direction of iteration and allow you to compute a value of a type different than that contained by any Array. This new type could be a struct, an object, or anything else available to you in Swift.

Use reduce when appropriate but remember foldl and foldr for when you need them. For a more realistic example of the need for fold see the next blog posting.

Advertisements

3 Comments »

  1. […] Programming News: Swift, Extensions, Single Value Tuples, Passing Closures, and Functional Programming Swift allows us to extend existing types. This is one of the features of Objective-C that I really like. Because existing types can be modified, I can add methods, and computed properties in the case of Swift, to instances of entities that have been created in a library other than my own. This would not be directly possible using the inheritance model since the library’s provider would not be able to instantiate objects of my sub-class. Read full story => TetonTech […]

  2. Trout said,

    “Reduce always returns the same type as is found in the array.” This is not true, reduce returns the type of the accumulator:

    func reduce(sequence: S, initial: U, combine: (U, S.Generator.Element) -> U) -> U

    • tetontech said,

      Such is the nature of commenting on beta languages. It is good to know this limitation was removed. Thanks.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: