Why I Love Swift: Functional Programming

Apple’s Swift language, which is not even a year old, has grown on me in ways I would never have expected. Partly, that’s because it has opened my eyes to ways of programming I was unaware of.

One of the programming paradigms that Swift forced me to at least look at was functional programming. What’s functional programming? Well, here’s a post by Guanshan Liu, Functional Programming in Swift, that does a much better job than I could ever do. And there’s a great book, Functional Programming in Swift. Simply put, functional programming allows functions to be used as parameters within a function call. Is that very useful or just another egg-head, ivory-tower CompSci methodology that no app developer really needs? Hardly.

Let’s say within a HomeKit app I’m creating that I have an array of accessories (home-automation devices) and their services. Now, that and a nickel won’t get me a cup of coffee nor do much for any user of my HomeKit app. So I want to use the list of the services’ serviceType, mind you in the same order as Apple’s service types supported by HomeKit’s Accessory profile, and the devices that have those service types. Hmmm…To a novice programmer, one way to obtain the above would be,

        let roomAccessories: [HMAccessory]         = currentHome.roomForEntireHome().accessories as! [HMAccessory]

        // This returns the services of all accessories in the room.
        var roomAccessoryServicesArray:[AnyObject] = []
        for roomAccessory in roomAccessories
        {
            let accessoryServices       = roomAccessory.services
            roomAccessoryServicesArray.append(accessoryServices)
        }

        // This returns an array of of the service types of all accessories
        var roomAccessoryServices: [AnyObject]	= []
        for roomAccessory in roomAccessories
        {
            for roomAccessoryService in roomAccessoryServicesArray
            {
                roomAccessoryServices.append(roomAccessoryService)
            }
        }

        // This returns an array of service type names with any duplicates removed
        var roomAccessoryServiceTypes: [String]	= []
        for roomAccessoryService in roomAccessoryServices
        {
            roomAccessoryServiceTypes.append(roomAccessoryService.serviceType)
        }
        roomAccessoryServiceTypes       	  	= Array(Set(roomAccessoryServiceTypes))

        let currentServicesTypes: [String]    	= self.reorderArrayToBaseArray(inputArray: roomAccessoryServiceTypes, baseArray: availableAccessoryServiceTypes)

        currentServicesTypes.map({println("localizedServiceType: \(HMService.hws_localizedDescriptionForServiceType($0))")})

Yikes!!!

Functional programming will help with this in a big way. More specifically, Swift’s support for two higher-order function, map and reduce will do all of the drudgery done above and reduce it all to…,

        let roomAccessories             = currentHome.roomForEntireHome().accessories as! [HMAccessory]

        // This returns the services of all accessories in the room.
        var roomAccessoryServicesArray  = roomAccessories.map({$0.services})

        // This returns an array of of the service types of all accessories
        var roomAccessoryServices  		= reduce(map(roomAccessoryServicesArray, {$0}), [], +)

        // This returns an array of service type names with any duplicates removed
        var roomAccessoryServiceTypes   = Array(Set(roomAccessoryServices.map({$0.serviceType as String})))

        let currentServicesTypes: [String]    = self.reorderArrayToBaseArray(inputArray: roomAccessoryServiceTypes, baseArray: availableAccessoryServiceTypes)

        currentServicesTypes.map({println("localizedServiceType: \(HMService.hws_localizedDescriptionForServiceType($0)")})

Ok, that’s better. In fact, I could live with this. The calls of the higher-order functions map and reduce have greatly reduced the clutter. And then there’s the Array(Set(anArray)) hack to remove duplicates within an array.

But I can do more. And how would a much more functional approach look? How powerful is it?

Well, all of the above code can be compressed into,

self.reorderArrayToBaseArray(inputArray: Array(Set(reduce(map((currentHome.roomForEntireHome().accessories as! [HMAccessory]).map({$0.services}), {$0}), [], +).map({$0.serviceType as String}))), baseArray: availableAccessoryServiceTypes).map({println("localizedServiceType: \(HMService.hws_localizedDescriptionForServiceType($0))")})

Ok, this isn’t very readable. But it does show that with Swift’s support for functional programming, you can compress many lines of code into one call.

Advertisements