Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Think Functionally in Swift

Ievgen Salo
November 27, 2014

Think Functionally in Swift

Ievgen Salo

November 27, 2014
Tweet

More Decks by Ievgen Salo

Other Decks in Programming

Transcript

  1. Agenda 1. Swift history 2. A bit of syntax 3.

    Better than Obj-C or not? 4. Functional Programming 5. Main principles 6. Examples
  2. Swift History Development began in 2010 by Chris Lattner First

    release - June 2, 2014 Current version - 1.1 Swift Playgrounds and Swift REPL
  3. Type Inference let languageName = "Swift" // inferred as String

    var version = 1.0 // inferred as Double let introduced = 2014 // inferred as Int let isAwesome = true // inferred as Bool
  4. Typed Collections var names = ["Anna", "Alex", "Brian", "Jack"] //

    an array of String values var numberOfLegs = ["ant": 6, "snake": 0, "cheetah": 4] // a Dictionary with String keys and Int values var names = ["Anna", "Alex", "Brian", "Jack", Bicycle()] // not working
  5. Optionals let possibleNumber = "123" let convertedNumber = possibleNumber.toInt() //

    convertedNumber is inferred to be of type "Int?", or "optional Int
  6. Optionals let possibleNumber = "123" let convertedNumber = possibleNumber.toInt() //

    convertedNumber is inferred to be of type "Int?", or "optional Int if let actualNumber = possibleNumber.toInt() { println("\(possibleNumber) has an integer value of \(actualNumber)") } else { println("\(possibleNumber) could not be converted to an integer") } // prints "123 has an integer value of 123
  7. Fast and Powerfull var score = 570 switch score {

    case 1..<10: println("novice") case 10..<100: println("proficient") case 100..<1000: println("rock-star") default: println("awesome") }
  8. Modern func sayHello(name: String = "World") { println("Hello \(name)!") }

    sayHello() sayHello(name: "DataArt") var sortedStrings = sorted(stringArray) { $0.uppercaseString < $1.uppercaseString }
  9. Modern • Tuples and multiple return values func refreshWebPage() ->

    (Int, String) { // ...try to refresh... return (200, "Success") } let (statusCode, message) = refreshWebPage()
  10. Modern • Closures let animals = ["fish", "cat", "chicken", "dog"]

    animals.sorted({ // no return keyword one, two in one > two }) // shorthand arguments animals.sorted({ $0 > $1 }) // trailing closure animals.sorted { $0 > $1 }
  11. Modern • Generics • Fast and concise iteration over a

    range or collection • Structs that support methods, extensions, protocols. • Functional programming patterns, e.g.: map and filter
  12. Test 11 1 For more details check this "An analysis

    of sorts between Objective-C and Swift" .
  13. Test 21 1 For more details check this "An analysis

    of sorts between Objective-C and Swift" .
  14. Test 31 1 For more details check this "An analysis

    of sorts between Objective-C and Swift" .
  15. Main Concepts • No "loops" • No "if" statements •

    Cannot mutate data • No "side effects"
  16. func incrementArray(xs: [Int]) -> [Int] { var result: [Int] =

    [] for x in xs { result.append(x + 1) } return result }
  17. func doubleArray(xs: [Int]) -> [Int] { var result: [Int] =

    [] for x in xs { result.append(x * 2) } return result }
  18. func computeIntArray(xs: [Int], f: Int -> Int) -> [Int] {

    var result: [Int] = [] for x in xs { result.append(f(x)) } return result }
  19. func incrementArray(xs: [Int]) -> [Int] { return computeIntArray(xs) { x

    in x + 1 } } func doubleArray(xs: [Int]) -> [Int] { return computeIntArray(xs) { x in x * 2 } }
  20. func isEvenArray(xs: [Int]) -> [Bool] { computeIntArray(xs) { x in

    x % 2 == 0 } } func computeBoolArray(xs: [Int], f: Int -> Bool) -> [Int] { var result: [Int] = [] for x in xs { result.append(f(x)) } return result }
  21. Map func map<T, U>(xs: [T], f: T -> U) ->

    [U] { var result: [U] = [] for x in xs { result.append(f(x)) } return result } func computeIntArray<T>(xs: [Int], f: Int -> T) -> [T] { return map(xs, f) }
  22. Currying func addNumbers(one:Int, two:Int, three:Int) -> Int { return one

    + two + three } let sum = addNumbers(2, 5, 4) // 11 func curryAddNumbers(one:Int)(two:Int)(three:Int) -> Int { return one + two + three } let stepOne = curryAddNumbers(2) let stepTwo = stepOne(two: 5) let result = stepTwo(three: 4) // 11 let result2 = curryAddNumbers(2)(two: 5)(three: 4) // 11
  23. Currying func curriedPadding(startingAtIndex: Int, withString: String) (source: String, length: Int)

    -> String { return source.stringByPaddingToLength(length, withString: withString, startingAtIndex: startingAtIndex); } let dotPadding = curriedPadding(0, ".") let dotPadded = dotPadding(source: "Curry!", length: 10)
  24. Filter func getSwiftFiles(files: [String]) -> [String] { var result: [String]

    = [] for file in files { if file.hasSuffix(".swift") { result.append(file) } } return result }
  25. Filter func filter<T>(xs: [T], check: T -> Bool) -> [T]

    { var result: [T] = [] for x in xs { if check(x) { result.append(x) } } }
  26. Reduce func sum(xs: [Int]) -> Int { var result: Int

    = 0 for x in xs { result += x } return result }
  27. Reduce func reduce<A, R>(arr: [A], initialValue: R, combine: (R, A)

    -> R) -> R { var result = initialValue for i in arr { result = combine(result, i) } return result }
  28. Filter + Reduce var evens = [Int]() for i in

    1...10 { if i % 2 == 0 { evens.append(i) } } var evenSum = 0 for i in evens { evenSum += i } println(evenSum) //30
  29. Filter + Reduce evenSum = Array(1...10) .filter { (number) in

    number % 2 == 0 } .reduce(0) { (total, number) in total + number } println(evenSum) //30
  30. Building an index let words = ["Cat", "Chicken", "fish", "Dog",

    "Mouse", "Guinea Pig", "monkey"] typealias Entry = (Character, [String]) func buildIndex(words: [String]) -> [Entry] { var result = [Entry]() var letters = [Character]() for word in words { let firstLetter = Character(word.substringToIndex( advance(word.startIndex, 1)).uppercaseString) if !contains(letters, firstLetter) { letters.append(firstLetter) } } for letter in letters { var wordsForLetter = [String]() for word in words { let firstLetter = Character(word.substringToIndex( advance(word.startIndex, 1)).uppercaseString) if firstLetter == letter { wordsForLetter.append(word) } } result.append((letter, wordsForLetter)) } return result }
  31. Building an index let words = ["Cat", "Chicken", "fish", "Dog",

    "Mouse", "Guinea Pig", "monkey"] typealias Entry = (Character, [String]) func distinct<T: Equatable>(source: [T]) -> [T] { var unique = [T]() for item in source { if !contains(unique, item) { unique.append(item) } } return unique } func buildIndex(words: [String]) -> [Entry] { let letters = words.map {(word) -> Character in Character(word.substringToIndex(advance(word.startIndex, 1)).uppercaseString) } let distinctLetters = distinct(letters) return distinctLetters.map { (letter) -> Entry in return (letter, words.filter { (word) -> Bool in Character(word.substringToIndex(advance(word.startIndex, 1)).uppercaseString) == letter }) } }
  32. Advantages 1. Cashing 2. Avoid side effect 3. Easy to

    parallelize 4. Easy to write tests 5. Compiler can optimize the code
  33. func parseBlog(blogDict: [String:AnyObject]) -> Blog? { if let id =

    blogDict["id"] as NSNumber? { if let name = blogDict["name"] as NSString? { if let needsPassword = blogDict["needspassword"] as NSNumber? { if let url = blogDict["url"] as NSString? { return Blog(id: id.integerValue, name: name, needsPassword: needsPassword.boolValue, url: NSURL(string: url) ) } } } return nil }
  34. Result func parseBlog(blogData: [String:AnyObject]) -> Blog? { let id =

    int(blogData,"id") let name = string(blogData,"name") let needsPassword = bool(blogData,"needspassword") let url = string(blogData,"url").map { NSURL(string:$0) } if let (id, name, needsPassword, url) = flatten(id, name, needsPassword, url) { return Blog(id: id, name: name, needsPassword: needsPassword, url: url) } return nil }
  35. Result func parse(blogData: [String:AnyObject]) -> Blog? { let makeBlog =

    curry { Blog(id: $0, name: $1, needsPassword: $2, url: $3) } return makeBlog <*> int(blogData,"id") <*> string(blogData,"name") <*> bool(blogData,"needspassword") <*> string(blogData,"url").map { NSURL(string:$0) } }