Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Functional Swift
Search
Chris Eidhof | @chriseidhof
August 07, 2014
Technology
1
140
Functional Swift
Łódź wiOSłuje - August, 2014
Chris Eidhof | @chriseidhof
August 07, 2014
Tweet
Share
More Decks by Chris Eidhof | @chriseidhof
See All by Chris Eidhof | @chriseidhof
Dutch FP Day 2015
chriseidhof
2
370
Tiny Networking in Swift
chriseidhof
2
19k
Functional Swift - Brooklyn
chriseidhof
3
1.2k
Functional Swift - SF
chriseidhof
6
26k
Functional Swift
chriseidhof
6
1.2k
Functional Programming in Swift
chriseidhof
40
19k
Lighter View Controllers
chriseidhof
4
190
Parsing with Blocks
chriseidhof
2
220
Practical Concurrent Programming
chriseidhof
4
260
Other Decks in Technology
See All in Technology
日本版とグローバル版のモバイルアプリ統合の開発の裏側と今後の展望
miichan
1
130
APIとはなにか
mikanichinose
0
100
NW-JAWS #14 re:Invent 2024(予選落ち含)で 発表された推しアップデートについて
nagisa53
0
270
[Ruby] Develop a Morse Code Learning Gem & Beep from Strings
oguressive
1
170
マイクロサービスにおける容易なトランザクション管理に向けて
scalar
0
140
Google Cloud で始める Cloud Run 〜AWSとの比較と実例デモで解説〜
risatube
PRO
0
110
どちらを使う?GitHub or Azure DevOps Ver. 24H2
kkamegawa
0
900
KubeCon NA 2024 Recap: How to Move from Ingress to Gateway API with Minimal Hassle
ysakotch
0
210
2024年にチャレンジしたことを振り返るぞ
mitchan
0
140
開発生産性向上! 育成を「改善」と捉えるエンジニア育成戦略
shoota
2
400
バクラクのドキュメント解析技術と実データにおける課題 / layerx-ccc-winter-2024
shimacos
2
1.1k
5分でわかるDuckDB
chanyou0311
10
3.2k
Featured
See All Featured
Fashionably flexible responsive web design (full day workshop)
malarkey
405
66k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.2k
How to train your dragon (web standard)
notwaldorf
88
5.7k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
28
2.1k
Code Review Best Practice
trishagee
65
17k
How STYLIGHT went responsive
nonsquared
95
5.2k
The Invisible Side of Design
smashingmag
298
50k
How to Think Like a Performance Engineer
csswizardry
22
1.2k
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
Raft: Consensus for Rubyists
vanstee
137
6.7k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
33
2k
RailsConf 2023
tenderlove
29
940
Transcript
Functional Swift @chriseidhof Łódź wiOSłuje - August, 2014
What's Functional Programming? • Pure • Referentially transparent • Typed
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)
Names > [Poznań, Warszawa, Wrocław, Kraków, Łódź]
Populations > [567932, 1706624, 632930, 766583, 753192]
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]
Filter func isLodz(s: String) -> Bool { return s ==
"Łódź" } names.filter(isLodz) > [Łódź]
Filter, simplified names.filter({ (s: String) -> Bool in return s
== "Łódź" }) > [Łódź]
Filter, more simplified names.filter({ s in return s == "Łódź"
}) > [Łódź]
Filter, even more simplified names.filter({ return $0 == "Łódź" })
> [Łódź]
Filter, simplest names.filter { $0 == "Łódź" } > [Łódź]
populations.filter { $0 > 1000000 } > [1706624]
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
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
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 }
Reduce reduce(0, +, Array(1..<10)) > 45 reduce(1, *, Array(1..<10)) >
362880
Sum and Product let sum = { reduce(0,+,$0) } let
product = { reduce(1,*,$0) }
Concatenate func concat(strings: [String]) -> String { var result =
"" for x in strings { result += x } return result } concat(names) > PoznańWarszawaWrocławKrakówŁódź
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ź
Adding line-breaks reduce("", { $0 + "\n" + $1 },
names) > Poznań > Warszawa > Wrocław > Kraków > Łódź
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 }
Example: Core Image
The Objective-C way CIFilter *hueAdjust = [CIFilter filterWithName:@"CIHueAdjust"]; [hueAdjust setDefaults];
[hueAdjust setValue: myCIImage forKey: kCIInputImageKey]; [hueAdjust setValue: @2.094f forKey: kCIInputAngleKey];
A Swift Filter typealias Filter = CIImage -> CIImage
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 } }
Example let url = NSURL(string: "http://bit.ly/1pabRsM"); let image = CIImage(contentsOfURL:
url) let blurBy5 = blur(5) let blurred = blurBy5(image)
None
Color Generator func colorGenerator(color: NSColor) -> Filter { return {
_ in let filter = CIFilter(name:"CIConstantColorGenerator", parameters: [kCIInputColorKey: color]) return filter.outputImage } }
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()) } }
Color Overlay func colorOverlay(color: NSColor) -> Filter { return {
image in let overlay = colorGenerator(color)(image) return compositeSourceOver(overlay)(image) } }
Combining everything let blurRadius = 5.0 let overlayColor = NSColor.redColor().colorWithAlphaComponent(0.2)
let blurredImage = blur(blurRadius)(image) let overlaidImage = colorOverlay(overlayColor)(blurredImage)
None
Combining everything, take 2 let result = colorOverlay(overlayColor)(blur(blurRadius)(image))
Filter composition func composeFilters(filter1: Filter, filter2: Filter) -> Filter {
return {img in filter1(filter2(img)) } }
Using filter composition let myFilter1 = composeFilters(blur(blurRadius), colorOverlay(overlayColor)) let result1
= myFilter1(image)
Filter composition with an operator infix operator |> { associativity
left } func |> (filter1: Filter, filter2: Filter) -> Filter { return {img in filter1(filter2(img))} }
Using filter composition let myFilter2 = blur(blurRadius) |> colorOverlay(overlayColor) let
result2 = myFilter2(image)
Function composition func |> (f1: B -> C, f2: A
-> B) -> A -> C { return {x in f1(f2(x))} }
Example: Spreadsheet
None
None
None
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(...) }
Parsing references let reference = { Token.Reference($0,$1) } </> capital
<*> naturalNumber
Parsing expressions prim = numberOrReference <|> functionCall <|> parens(expression)
Parsing expressions let operators : [[String]] = [ [":"] ,
["*", "/"] , ["+", "-"] ] let expression = pack(operators, prim)
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) ) )
Evaluating expressions
The result enum enum Result { case IntResult(Int) case StringResult(String)
case ListResult([Result]) case EvaluationError(String) }
The evaluation function func evaluate(expressions: [Expression?]) -> [Result] { return
expressions.map(evaluateExpression(expressions)) }
Evaluating an expression evaluateExpression([42,10*10,A0+A1])('A1') > 100 evaluateExpression([42,10*10,A0+A1])('A2') > 142
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") } }
Mixing FP and OO class SpreadsheetDatasource : NSObject, NSTableViewDataSource, EditedRow
Mixing FP and OO var arr: [String] func tableView(aTableView: NSTableView,
objectValueForTableColumn: NSTableColumn, row: Int) -> AnyObject { return editedRow == row ? arr[row] : results[row] }
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) }
Conclusion FP is a massively powerful tool in your toolbox.
Use it together with OO, and build awesome stuff.
By Chris Eidhof, Florian Kugler and Wouter Swierstra
@chriseidhof