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
130
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.1k
Functional Swift - SF
chriseidhof
6
26k
Functional Swift
chriseidhof
6
1.2k
Functional Programming in Swift
chriseidhof
40
14k
Lighter View Controllers
chriseidhof
4
180
Parsing with Blocks
chriseidhof
2
220
Practical Concurrent Programming
chriseidhof
4
250
Other Decks in Technology
See All in Technology
RubyKaigi 2024 - Make Your Own Regex Engine!
makenowjust
1
180
Money-saving tips for the frugal serverless developer
theburningmonk
1
410
令和版ソフトウェアエンジニアの情報収集術 PHPカンファレンス香川2024
ysknsid25
4
910
1Q86
kawaguti
PRO
2
190
AWSの生成AI入門書を執筆しました🎉
minorun365
PRO
0
150
複雑なビジネスルールに挑む:正確性と効率性を両立するfp-tsのチーム活用術 / Strike a balance between correctness and efficiency with fp-ts
kakehashi
5
3.7k
ハードウェアを動かすTypeScriptの世界
9wick
3
1.2k
Taking Flight with Tailwind CSS
opdavies
0
4.3k
iThome2024 Wailing Wall of Enterprise Security
notsurprised
0
300
多言語化対応における TypeScript の型定義を通して開発のしやすさについて考えた / TSKaigi TypeScript Multilingualization
nabeliwo
2
400
Dungeons and Dragons and Rails
joelq
0
260
AWS CLIの起動が重くてつらいので aws-sdk-client-go を書いた / kamakura.go#6
fujiwara3
6
3.4k
Featured
See All Featured
Why You Should Never Use an ORM
jnunemaker
PRO
51
8.7k
For a Future-Friendly Web
brad_frost
172
9k
Bootstrapping a Software Product
garrettdimon
PRO
302
110k
What's new in Ruby 2.0
geeforr
338
31k
Imperfection Machines: The Place of Print at Facebook
scottboms
261
12k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
126
32k
Intergalactic Javascript Robots from Outer Space
tanoku
266
26k
Code Review Best Practice
trishagee
56
15k
The Illustrated Children's Guide to Kubernetes
chrisshort
32
47k
How GitHub Uses GitHub to Build GitHub
holman
468
290k
Visualization
eitanlees
137
14k
The Language of Interfaces
destraynor
151
23k
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