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

Functional Swift

Functional Swift

Łódź wiOSłuje - August, 2014

Chris Eidhof | @chriseidhof

August 07, 2014
Tweet

More Decks by Chris Eidhof | @chriseidhof

Other Decks in Technology

Transcript

  1. Our data set let cities : [String:Int] = [ "Warszawa":

    1706624 , "Kraków": 766583 , "Łódź": 753192 , "Wrocław": 632930 , "Poznań": 567932 ] let names = Array(cities.keys) let populations = Array(cities.values)
  2. Map func addCity(s: String) -> String { return s +

    " is a city" } names.map(addCity) > [Poznań is a city, Warszawa is a city, Wrocław is a city, Kraków is a city, Łódź is a city]
  3. Filter func isLodz(s: String) -> Bool { return s ==

    "Łódź" } names.filter(isLodz) > [Łódź]
  4. Filter, simplest names.filter { $0 == "Łódź" } > [Łódź]

    populations.filter { $0 > 1000000 } > [1706624]
  5. Sum of an array func sum(arr: [Int]) -> Int {

    var result = 0 for i in arr { result += i } return result } sum(Array(1..<10)) > 45
  6. Product of an array func product(arr: [Int]) -> Int {

    var result = 1 for i in arr { result *= i } return result } product(Array(1..<10)) > 362880
  7. Reduce func reduce(initialValue: Int, combine: (Int,Int) -> Int, arr: [Int])

    -> Int { var result = initialValue for i in arr { result = combine(result,i) } return result }
  8. Concatenate func concat(strings: [String]) -> String { var result =

    "" for x in strings { result += x } return result } concat(names) > PoznańWarszawaWrocławKrakówŁódź
  9. Generics func reduce<A>(initialValue: A, combine: (A,A) -> A, arr: [A])

    -> A { var result = initialValue for i in arr { result = combine(result,i) } return result } reduce("", +, names) > PoznańWarszawaWrocławKrakówŁódź
  10. Adding line-breaks reduce("", { $0 + "\n" + $1 },

    names) > Poznań > Warszawa > Wrocław > Kraków > Łódź
  11. Making reduce more generic func reduce<A,R>(initialValue: R, combine: (R,A) ->

    R, arr: [A]) -> R { var result = initialValue for i in arr { result = combine(result,i) } return result }
  12. The Objective-C way CIFilter *hueAdjust = [CIFilter filterWithName:@"CIHueAdjust"]; [hueAdjust setDefaults];

    [hueAdjust setValue: myCIImage forKey: kCIInputImageKey]; [hueAdjust setValue: @2.094f forKey: kCIInputAngleKey];
  13. Blur func blur(radius: Double) -> Filter { return { image

    in let parameters : Parameters = [kCIInputRadiusKey: radius, kCIInputImageKey: image] let filter = CIFilter(name:"CIGaussianBlur", parameters:parameters) return filter.outputImage } }
  14. Color Generator func colorGenerator(color: NSColor) -> Filter { return {

    _ in let filter = CIFilter(name:"CIConstantColorGenerator", parameters: [kCIInputColorKey: color]) return filter.outputImage } }
  15. Composite Source Over func compositeSourceOver(overlay: CIImage) -> Filter { return

    { image in let parameters : Parameters = [kCIInputBackgroundImageKey: image, kCIInputImageKey: overlay] let filter = CIFilter(name:"CISourceOverCompositing", parameters: parameters) return filter.outputImage.imageByCroppingToRect(image.extent()) } }
  16. Color Overlay func colorOverlay(color: NSColor) -> Filter { return {

    image in let overlay = colorGenerator(color)(image) return compositeSourceOver(overlay)(image) } }
  17. Combining everything let blurRadius = 5.0 let overlayColor = NSColor.redColor().colorWithAlphaComponent(0.2)

    let blurredImage = blur(blurRadius)(image) let overlaidImage = colorOverlay(overlayColor)(blurredImage)
  18. Filter composition with an operator infix operator |> { associativity

    left } func |> (filter1: Filter, filter2: Filter) -> Filter { return {img in filter1(filter2(img))} }
  19. Function composition func |> (f1: B -> C, f2: A

    -> B) -> A -> C { return {x in f1(f2(x))} }
  20. Expressions enum Expression { case Number(Int) // e.g. 10 case

    Reference(String,Int) // A0 case BinaryExpression(String,Expression,Expression) // 1 + A9 case FunctionCall(String,Expression) // SUM(...) }
  21. Parsing expressions let operators : [[String]] = [ [":"] ,

    ["*", "/"] , ["+", "-"] ] let expression = pack(operators, prim)
  22. Parsing results We can now convert this: parse(expression, "SUM(A1:A9)") into

    this: Expression.FunctionCall("SUM", Expression.BinaryExpression( ":", Expression.Reference("A",1), Expression.Reference("A",9) ) )
  23. The result enum enum Result { case IntResult(Int) case StringResult(String)

    case ListResult([Result]) case EvaluationError(String) }
  24. Evaluating an expression func evaluateExpression(context: [Expression?]) -> Expression? -> Result

    { return {e in e.map { expression in let compute = evaluateExpression(context) switch (expression) { case .Number(let x): return Result.IntResult(x) case .Reference("A", let idx): return compute(context[idx]) case .BinaryExpression(let s, let l, let r): return evaluateBinary(s, compute, l, r) case .FunctionCall(let f, let p): return evaluateFunction(f, compute(p)) default: return .EvaluationError("Couldn't evaluate expression") } } ?? .EvaluationError("Couldn't parse expression") } }
  25. Mixing FP and OO var arr: [String] func tableView(aTableView: NSTableView,

    objectValueForTableColumn: NSTableColumn, row: Int) -> AnyObject { return editedRow == row ? arr[row] : results[row] }
  26. Mixing FP and OO func calculateExpressions() { let expressions: [Expression?]

    = arr.map { if let tokens = parse(tokenize(), $0) { return parse(expression(), tokens) } return nil } results = evaluate(expressions) }
  27. Conclusion FP is a massively powerful tool in your toolbox.

    Use it together with OO, and build awesome stuff.