June 8, 2014
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.
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 elements func 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 elements func 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;
- T – the type of the elements in the Array, and
- 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.
In the News: 2014-06-08 | Klaus' Korner said,
June 8, 2014 at 2:37 pm
[…] 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 […]
Trout said,
September 9, 2014 at 4:48 am
“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,
September 9, 2014 at 7:01 am
Such is the nature of commenting on beta languages. It is good to know this limitation was removed. Thanks.