Living in a Post-OOP world: Protocol Extensions in Swift 2


After watching Dave Abrahams (Professor of Blowing-Your-Mind) tell us about his friend Crusty in the WWDC 2015 video titled Protocol-Oriented Programming in Swift it suddenly fell into place what had happened to all of those generic functions (or algorithms) in Swift. The ones like map() and contains() and so on.

Apple hasn't created type methods for each of these and then hidden the originals (preventing mere mortals from accessing them), they've created protocol extensions to provide the illusion of this having happened.

Revealing the magic

In order to explain the magic, I went back to earlier posts and this one in particular, where I was greatly assisted in my learning by @al_skipp and @AirspeedSwift (AirspeedVelocity). In that post a deleteDuplicates() function was written for Strings and Arrays.
func deleteDuplicates<S:ExtensibleCollectionType where S.Generator.Element: Equatable>(seq:S)-> S {
    let s = seq.reduce(S()){
        ac, x in ac.contains(x) ? ac : ac + [x]
    }
    return s
}
And it's very simple to transform this into the new way of doing things:
extension ExtensibleCollectionType where Generator.Element: Equatable {

    mutating func deleteDuplicates() {
        let s = self.reduce(Self()){
            ac, x in ac.contains(x) ? ac : ac + [x]
        }
        self = s
    }

}
All the logic that was formerly placed after the function name now gets placed after the extension keyword, and we can place as many methods inside the braces as demand the same conditions. The only thing to point out is that the self (written all lowercase) refers to the instance and Self (with an initial cap) refers to the type.

Note: While we could write Self.Generator.Element: Equatable the Self part is inferred and so can be omitted.

What does it all mean?

The change means that no longer do we employ the function like this:
let str = "Hello, playground"
let arr = [1,2,3,4,5,6,6,5,4,3]

deleteDuplicates(str)
deleteDuplicates(arr)
But call the method like this instead for an array:
var arr = [1,2,3,4,5,6,6,5,4,3]


arr.deleteDuplicates()
And like this for a string
var str = "Hello, playground"

var charView = str.characters
charView.deleteDuplicates()
String(charView) // "Helo, paygrund"
Note: The reason things have become complicated here for a string is because, as with counting in Swift 2, we can only access the characters through the characters property and not through the string directly. But this is a side issue and shouldn't be held against protocol extensions, because this complication would've happened to the use of the generic function anyway with the new way in which strings are manipulated.

Conclusion

The changes which protocol extensions enable makes methods more discoverable and if we didn't know better then we'd think these were type methods. [Insert Paul Daniels' catchphrase here.]

(Note: I've also taken the opportunity of transforming the method into a mutating one.)



Endorse on Coderwall

Comments

  1. did't work in beta1. Compile error "'String' does not have a member named 'deleteDuplicates'"

    ReplyDelete
    Replies
    1. Well spotted, I forgot to take account of the new use of the characters property to return a CharacterView. I've updated the article.

      Delete
  2. This comment has been removed by the author.

    ReplyDelete

Post a Comment