Slide 1

Slide 1 text

Functional Swift @chriseidhof Łódź wiOSłuje - August, 2014

Slide 2

Slide 2 text

What's Functional Programming? • Pure • Referentially transparent • Typed

Slide 3

Slide 3 text

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)

Slide 4

Slide 4 text

Names > [Poznań, Warszawa, Wrocław, Kraków, Łódź]

Slide 5

Slide 5 text

Populations > [567932, 1706624, 632930, 766583, 753192]

Slide 6

Slide 6 text

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]

Slide 7

Slide 7 text

Filter func isLodz(s: String) -> Bool { return s == "Łódź" } names.filter(isLodz) > [Łódź]

Slide 8

Slide 8 text

Filter, simplified names.filter({ (s: String) -> Bool in return s == "Łódź" }) > [Łódź]

Slide 9

Slide 9 text

Filter, more simplified names.filter({ s in return s == "Łódź" }) > [Łódź]

Slide 10

Slide 10 text

Filter, even more simplified names.filter({ return $0 == "Łódź" }) > [Łódź]

Slide 11

Slide 11 text

Filter, simplest names.filter { $0 == "Łódź" } > [Łódź] populations.filter { $0 > 1000000 } > [1706624]

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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 }

Slide 15

Slide 15 text

Reduce reduce(0, +, Array(1..<10)) > 45 reduce(1, *, Array(1..<10)) > 362880

Slide 16

Slide 16 text

Sum and Product let sum = { reduce(0,+,$0) } let product = { reduce(1,*,$0) }

Slide 17

Slide 17 text

Concatenate func concat(strings: [String]) -> String { var result = "" for x in strings { result += x } return result } concat(names) > PoznańWarszawaWrocławKrakówŁódź

Slide 19

Slide 19 text

Adding line-breaks reduce("", { $0 + "\n" + $1 }, names) > Poznań > Warszawa > Wrocław > Kraków > Łódź

Slide 20

Slide 20 text

Making reduce more generic func reduce(initialValue: R, combine: (R,A) -> R, arr: [A]) -> R { var result = initialValue for i in arr { result = combine(result,i) } return result }

Slide 21

Slide 21 text

Example: Core Image

Slide 22

Slide 22 text

The Objective-C way CIFilter *hueAdjust = [CIFilter filterWithName:@"CIHueAdjust"]; [hueAdjust setDefaults]; [hueAdjust setValue: myCIImage forKey: kCIInputImageKey]; [hueAdjust setValue: @2.094f forKey: kCIInputAngleKey];

Slide 23

Slide 23 text

A Swift Filter typealias Filter = CIImage -> CIImage

Slide 24

Slide 24 text

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 } }

Slide 25

Slide 25 text

Example let url = NSURL(string: "http://bit.ly/1pabRsM"); let image = CIImage(contentsOfURL: url) let blurBy5 = blur(5) let blurred = blurBy5(image)

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

Color Generator func colorGenerator(color: NSColor) -> Filter { return { _ in let filter = CIFilter(name:"CIConstantColorGenerator", parameters: [kCIInputColorKey: color]) return filter.outputImage } }

Slide 28

Slide 28 text

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()) } }

Slide 29

Slide 29 text

Color Overlay func colorOverlay(color: NSColor) -> Filter { return { image in let overlay = colorGenerator(color)(image) return compositeSourceOver(overlay)(image) } }

Slide 30

Slide 30 text

Combining everything let blurRadius = 5.0 let overlayColor = NSColor.redColor().colorWithAlphaComponent(0.2) let blurredImage = blur(blurRadius)(image) let overlaidImage = colorOverlay(overlayColor)(blurredImage)

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

Combining everything, take 2 let result = colorOverlay(overlayColor)(blur(blurRadius)(image))

Slide 33

Slide 33 text

Filter composition func composeFilters(filter1: Filter, filter2: Filter) -> Filter { return {img in filter1(filter2(img)) } }

Slide 34

Slide 34 text

Using filter composition let myFilter1 = composeFilters(blur(blurRadius), colorOverlay(overlayColor)) let result1 = myFilter1(image)

Slide 35

Slide 35 text

Filter composition with an operator infix operator |> { associativity left } func |> (filter1: Filter, filter2: Filter) -> Filter { return {img in filter1(filter2(img))} }

Slide 36

Slide 36 text

Using filter composition let myFilter2 = blur(blurRadius) |> colorOverlay(overlayColor) let result2 = myFilter2(image)

Slide 37

Slide 37 text

Function composition func |> (f1: B -> C, f2: A -> B) -> A -> C { return {x in f1(f2(x))} }

Slide 38

Slide 38 text

Example: Spreadsheet

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

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(...) }

Slide 43

Slide 43 text

Parsing references let reference = { Token.Reference($0,$1) } capital <*> naturalNumber

Slide 44

Slide 44 text

Parsing expressions prim = numberOrReference <|> functionCall <|> parens(expression)

Slide 45

Slide 45 text

Parsing expressions let operators : [[String]] = [ [":"] , ["*", "/"] , ["+", "-"] ] let expression = pack(operators, prim)

Slide 46

Slide 46 text

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) ) )

Slide 47

Slide 47 text

Evaluating expressions

Slide 48

Slide 48 text

The result enum enum Result { case IntResult(Int) case StringResult(String) case ListResult([Result]) case EvaluationError(String) }

Slide 49

Slide 49 text

The evaluation function func evaluate(expressions: [Expression?]) -> [Result] { return expressions.map(evaluateExpression(expressions)) }

Slide 50

Slide 50 text

Evaluating an expression evaluateExpression([42,10*10,A0+A1])('A1') > 100 evaluateExpression([42,10*10,A0+A1])('A2') > 142

Slide 51

Slide 51 text

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") } }

Slide 52

Slide 52 text

Mixing FP and OO class SpreadsheetDatasource : NSObject, NSTableViewDataSource, EditedRow

Slide 53

Slide 53 text

Mixing FP and OO var arr: [String] func tableView(aTableView: NSTableView, objectValueForTableColumn: NSTableColumn, row: Int) -> AnyObject { return editedRow == row ? arr[row] : results[row] }

Slide 54

Slide 54 text

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) }

Slide 55

Slide 55 text

Conclusion FP is a massively powerful tool in your toolbox. Use it together with OO, and build awesome stuff.

Slide 56

Slide 56 text

By Chris Eidhof, Florian Kugler and Wouter Swierstra

Slide 57

Slide 57 text

@chriseidhof