Isolation of side-effects
// Pure, functional world
let emailChanged: Signal
let passwordChanged: Signal
let loginButtonEnabled = combineLatest(emailChanged, passwordChanged)
.map { !$0.isEmpty && !$1.isEmpty }
// Side-effect world
loginButtonEnabled.observeNext { [weak self] in
self?.loginButtonEnabled.enabled = $0
}
Slide 11
Slide 11 text
Isolation of side-effects
// Pure, functional world
let emailChanged: Signal
let passwordChanged: Signal
let loginButtonEnabled = combineLatest(emailChanged, passwordChanged)
.map { !$0.isEmpty && !$1.isEmpty }
// Side-effect world
self.loginButton.rac.enabled = loginButtonEnabled
Slide 12
Slide 12 text
Surfacing of co-effects
Slide 13
Slide 13 text
Surfacing of co-effects
????????????????
Slide 14
Slide 14 text
Surfacing of co-effects
If an effect is a change to the outside world after executing an
expression...
Slide 15
Slide 15 text
Surfacing of co-effects
If an effect is a change to the outside world after executing an
expression...
...then...
Slide 16
Slide 16 text
Surfacing of co-effects
If an effect is a change to the outside world after executing an
expression...
...then...
...a co-effect is the state of the world that the expression
needs in order to execute.
Surfacing of co-effects
References
— Colin Barrett
— Functional Swift Conference 2015
— Structure and Interpretation of Swift Programs
— The work of Tomas Petricek
— Coeffects: A calculus of context-dependent
computation
— Coeffects: The next big programming challenge
Slide 21
Slide 21 text
Effect/Co-effect Duality
Slide 22
Slide 22 text
Code to the interface you wish you
had, not the interface you were
given.
- Stephen Celis
Slide 23
Slide 23 text
An interface we were given
Slide 24
Slide 24 text
An interface we were given
Storyboards
— Very thick abstraction layer
— Separates code from data
— Constantly catching up to what UIKit can do
Slide 25
Slide 25 text
An interface we wish we had
Lenses
Slide 26
Slide 26 text
An interface we wish we had
Lenses
struct Project {
let creator: User
let id: Int
let name: String
}
Slide 27
Slide 27 text
An interface we wish we had
Lenses
Project.lens.name // => Lens
Slide 28
Slide 28 text
An interface we wish we had
Lenses
Project.lens.name // => Lens
Project.lens.name .~ "Advanced Swift" // => Project -> Project
Slide 29
Slide 29 text
An interface we wish we had
Lenses
Project.lens.name // => Lens
Project.lens.name .~ "Advanced Swift" // => Project -> Project
project
|> Project.lens.name .~ "Advanced Swift"
Slide 30
Slide 30 text
An interface we wish we had
Lenses
project
|> Project.lens.name .~ "Advanced Swift"
|> Project.lens.creator.name .~ "Chris Eidhof"
Slide 31
Slide 31 text
An interface we wish we had
UIKit Lenses
Slide 32
Slide 32 text
An interface we wish we had
UIKit Lenses
UIView.lens.backgroundColor // => Lens
Slide 33
Slide 33 text
An interface we wish we had
UIKit Lenses
UIView.lens.backgroundColor // => Lens
UIView.lens.backgroundColor .~ .redColor() // => UIView -> UIView
Slide 34
Slide 34 text
An interface we wish we had
UIKit Lenses
UIView.lens.backgroundColor // => Lens
UIView.lens.backgroundColor .~ .redColor() // => UIView -> UIView
view
|> UIView.lens.backgroundColor .~ .redColor()
|> UIView.lens.layer.cornerRadius .~ 4
|> UIView.lens.layer.masksToBounds .~ true
Slide 35
Slide 35 text
An interface we wish we had
UIKit Lenses
func roundedStyle(cornerRadius: CGFloat) -> (UIView) -> UIView {
return UIView.lens.layer.cornerRadius .~ 4
<> UIView.lens.layer.masksToBounds .~ true
}
view
|> roundedStyle(cornerRadius: 4)
|> UIView.lens.backgroundColor .~ .redColor()
Slide 36
Slide 36 text
An interface we wish we had
UIKit Lenses
let baseButtonStyle =
roundedStyle(cornerRadius: 4)
<> UIButton.lens.titleLabel.font .~ UIFont(size: 16)
<> UIButton.lens.contentEdgeInsets .~ .init(topBottom: 6, leftRight: 12)
let greenButtonStyle =
baseButtonStyle
<> UIButton.lens.backgroundColor(forState: .Normal) .~ .greenColor()
Slide 37
Slide 37 text
An interface we wish we had
UIKit Lenses
let bigButtonStyle =
baseButtonStyle
<> UIButton.lens.contentEdgeInsets %~ {
.init(top: $0.top * 2,
left: $0.left * 2,
bottom: $0.bottom * 2,
right: $0.right * 2)
}
Slide 38
Slide 38 text
An interface we wish we had
UIKit Lenses
let baseButtonStyle =
roundedStyle(cornerRadius: 4)
<> UIButton.lens.titleLabel.font %~~ { _, button in
button.traitCollection.verticalSizeClass == .Compact
? UIFont(size: 12)
: UIFont(size: 14)
}
<> UIButton.lens.contentEdgeInsets .~ .init(topBottom: 6, leftRight: 12)
Slide 39
Slide 39 text
Principles that we did not benefit so much from:
— D.R.Y.
— S.R.P.
— S.O.L.I.D.
— Objects
Slide 40
Slide 40 text
The Result
Slide 41
Slide 41 text
Testing
Slide 42
Slide 42 text
Test-Driven Development
Slide 43
Slide 43 text
Test-Driven Bug Fixing
Slide 44
Slide 44 text
Playground-Driven
Development
Slide 45
Slide 45 text
Screenshot testing
Slide 46
Slide 46 text
Event Tracking
Slide 47
Slide 47 text
Event Tracking
Slide 48
Slide 48 text
Accessibility
Slide 49
Slide 49 text
Love for UIKit
Slide 50
Slide 50 text
Be!er working relationship with Product Managers, Designers and Engineers
Slide 51
Slide 51 text
Finding Happiness in Functional
Programming
Slide 52
Slide 52 text
Finding Happiness in Functional
Programming
brandon@kickstarter.com