stanfy madcode meetup #9: functional programming 101 with swift
TRANSCRIPT
Stanfy MadCode #9
Alexander Voronov iOS Developer @ Stanfy
https://www.facebook.com/MadCodeMeetup
Functional Programming 101
With Swift
What is Functional Programming?
Functional Programming• Higher-order functions
Functional Programming• Higher-order functions
• Immutable states & pure functions
Functional Programming• Higher-order functions
• Immutable states & pure functions
• Declarative
Functional Programming• Higher-order functions
• Immutable states & pure functions
• Declarative
• Modularity
Functional Programming• Higher-order functions
• Immutable states & pure functions
• Declarative
• Modularity
• Types
Swift Power
Swift Power• First Class Functions
Swift Power• First Class Functions
• Currying
Swift Power• First Class Functions
• Currying
• Generics
Swift Power• First Class Functions
• Currying
• Generics
• Type Inference
Swift Power• First Class Functions
• Currying
• Generics
• Type Inference
• Enums
First Class Functionsfunc add(x: Int) -> Int -> Int { return { y in y + x } }
let addOne = add(1) addOne(2)
// 3
First Class Functionsfunc addTwo(x: Int) -> Int { return x + 2 }
(1...5).map(addTwo)
// [3, 4, 5, 6, 7]
Curryingfunc add(a: Int)(b: Int) -> Int { return a + b }
let addOne = add(1) let xs = 1...5 xs.map(addOne)
// [2, 3, 4, 5, 6]
Currying
func curry<A, B, C>(f: (A, B) -> C) -> A -> B -> C {
return { a in { b in f(a, b) } } }
Generics
func printEach<T: SequenceType>(items: T) { for item in items { print(item) } }
printEach(1...5) printEach(["one", "two", "three"])
Type Inference
let x = 42.0 x.dynamicType // Double x is Double // true
Type Inference
var xs = [1, 5, 2, 4, 3] xs.sort(<) print(xs) // [1, 2, 3, 4, 5]
Type Inference
var xs = [1, 5, 2, 4, 3] xs.sort(<) print(xs) // [1, 2, 3, 4, 5]
Type Inference
let xs = [1, 5, 2, 4, 3] let ys = xs.sorted(<) print(xs) // [1, 5, 2, 4, 3] print(ys) // [1, 2, 3, 4, 5]
Type Inference
let xs = [1, 5, 2, 4, 3] let ys = xs.sorted(<) print(xs) // [1, 5, 2, 4, 3] print(ys) // [1, 2, 3, 4, 5]
Enumerationsenum Fruit: String { case Apple = "apple" case Banana = "banana" case Cherry = "cherry" }
Fruit.Apple.rawValue // "apple"
Enumerations
enum ValidationResult { case Valid case NotValid(NSError) }
Enumerationsenum MyApi { case xAuth(String, String) case GetUser(Int) } extension MyApi: MoyaTarget { var baseURL: NSURL { return NSURL(string: "")! }
var path: String { switch self { case .xAuth: return "/authorise" case .GetUser(let id): return "/user/\(id)" } } }
https://github.com/Moya/Moya
Optionalsenum Optional<T> { case None case Some(T) }
var x = 5 x = nil // Error!
Optional Chainingstruct Dog { var name: String } struct Person { var dog: Dog? }
let dog = Dog(name: "Dodge") let person = Person(dog: dog) let dogName = person.dog?.name
Optional Chainingstruct Dog { var name: String } struct Person { var dog: Dog? }
let dog = Dog(name: "Dodge") let person = Person(dog: dog) let dogName = person.dog?.name
Optional Chaining
Functors, Applicatives,
Monads
Functors, Applicatives, Monads
let x = 2 x + 3 // == 5
Functors, Applicatives, Monads
let x = 2 x + 3 // == 5
let y = Optional(2)
Functors
let y = Optional(2) y + 3
// Error! // Value of optional type // Optional<Int> not unwrapped
Functors
let y = Optional(2) y.map { $0 + 3 }
// Optional(5)
Definition
Optional<T> == T?
Functors
func map<U>(f: T -> U) -> Optional<U> { switch self { case .Some(let x): return f(x) case .None: return .None } }
Functors
infix operator <^> { associativity left
}
func <^> <T, U>(f: T -> U, a: Optional<T>) -> Optional<U> { return a.map(f) }
Functors
func <^><T, U>(f: T -> U, a: Optional<T>) -> Optional<U> { return a.map(f) }
let addOne = { $0 + 1 }
addOne <^> Optional(2) // Optional(3)
Functors
func <^><T, U>(f: T -> U, a: Optional<T>) -> Optional<U> { return a.map(f) }
let addOne = { $0 + 1 }
addOne <^> Optional(2) // Optional(3)
add <^> Optional(2) <^> Optional(3)
Functors
func <^><T, U>(f: T -> U, a: Optional<T>) -> Optional<U> { return a.map(f) }
let addOne = { $0 + 1 }
addOne <^> Optional(2) // Optional(3)
add <^> Optional(2) <^> Optional(3)
Applicative
func apply<U>(f: Optional<(T -> U)>) -> Optional<U> { switch f { case .Some(let someF): return self.map(someF) case .None: return .None } }
Applicatives
infix operator <*> { associativity left }
func <*><T, U>(f: Optional<(T -> U)>, a: Optional<T>) -> Optional<U> { return a.apply(f) }
Applicatives
infix operator <*> { associativity left }
func <*><T, U>(f: Optional<(T -> U)>, a: Optional<T>) -> Optional<U> { return a.apply(f) }
func add(a: Int)(b: Int) -> Int { return a + b }
Applicatives
add <^> Optional(2) <*> Optional(3) // Optional(5)
Applicatives
add <^> Optional(2) <*> Optional(3) // Optional(5)
let a = add <^> Optional(2)
Applicatives
add <^> Optional(2) <*> Optional(3) // Optional(5)
let a = add <^> Optional(2)
let a: (b: Int) -> Optional<Int>
Monadstypealias T = Double let f: T -> T = { $0 * 2.0 } let g: (T, T) -> T = { $0 / $1 }
Monadstypealias T = Double let f: T -> T = { $0 * 2.0 } let g: (T, T) -> T = { $0 / $1 }
f(g(4, 2))
Monadstypealias T = Double let f: T -> T = { $0 * 2.0 } let g: (T, T) -> T = { $0 / $1 }
f(g(4, 2))
g: (T, T) -> Optional<T>
Monadstypealias T = Double let f: T -> T = { $0 * 2.0 } let g: (T, T) -> T = { $0 / $1 }
f(g(4, 2))
g: (T, T) -> Optional<T>
g(4, 2) >>- { f($0) }
Monads
>>-==
http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
Monads
func flatten<U>(a: Optional<Optional<U>>) -> Optional<U> { switch a { case .Some(let someA): return someA case .None: return .None } }
Monadsfunc flatMap<U>(f: T -> Optional<U>) -> Optional<U> { return flatten(map(f)) }
Monadsfunc flatMap<U>(f: T -> Optional<U>) -> Optional<U> { return flatten(map(f)) }
func map<U>(f: T -> U) -> Optional<U>
Monadsfunc flatMap<U>(f: T -> Optional<U>) -> Optional<U> { return flatten(map(f)) }
func map<U>(f: T -> U) -> Optional<U>
Monadsfunc flatMap<U>(f: T -> Optional<U>) -> Optional<U> { return flatten(map(f)) }
func map<U>(f: T -> U) -> Optional<U>
func map<Optional<U>>(f: T -> Optional<U>) -> Optional<Optional<U>>
Monads
infix operator >>- { associativity left }
func >>-<T, U>(a: Optional<T>, f: T -> Optional<U>) -> Optional<U> { return a.flatMap(f) }
Monadsfunc half(a: Int) -> Optional<Int> { return a % 2 == 0 ? a / 2 : .None }
Monadsfunc half(a: Int) -> Optional<Int> { return a % 2 == 0 ? a / 2 : .None }
Optional(8) >>- half >>- half // Optional(2)
Monad Laws
• Left Identity
• Right Identity
• Associativity
Left Identity Lawlet f = { Optional($0 + 1) } let a = 1
let x = Optional(a) >>- f let y = f(a)
x == y
Right Identity Law
func create<T>(value: T) -> Optional<T> { return Optional(value) }
let x = Optional(1) >>- create let y = Optional(1)
x == y
Associativity Law
let double = { Optional(2 * $0) } let triple = { Optional(3 * $0) }
let x = Optional(1) >>- double >>- triple let y = Optional(1) >>- { double($0) >>- triple } let z = { Optional(1) >>- double }() >>- triple
x == y y == z
RecapFunctor map<U>(f: T -> U) -> M<U>
Applicative apply<U>(f: M<(T -> U)>) -> M<U>
Monad flatMap<U>(f: T -> M<U>) -> M<U>
Pipesinfix operator |> { associativity left }
public func |> <T, U>(x: T, f: T -> U) -> U { return f(x) }
let addTwo = { $0 + 2 } let prodThree = { $0 * 3 }
5 |> addTwo |> prodThree |> print // 21
Pipeslet transformedX = x |> addTwo |> prodThree |> increment |> square |> pow
VS (pow(square(increment(prodThree(addTwo(x))))))
Railways
http://fsharpforfunandprofit.com/posts/recipe-part2/
Argo JSON Parser
+
Argo Runes
Argo Exampleextension Model: Decodable { static func decode(json: JSON) -> Decoded<Model> {
return Model.create <^> json <| "id" <*> json <| "room" <*> json <| "guest_name" <*> json <| "status" <*> json <| "label" <*> json <|? "user_comment" <*> json <| ["channel", "label"] <*> json <| "severity" <*> json <|| "epochs" <*> json <|| "body" } }
Argo Example
let entities: [Model]? entities = data?.json() >>- { $0["entities"] } >>- decode
ReactiveCocoa Example
valueLabel.rac_text <~ viewModel.selectedIndex.producer |> ignoreNil |> combineLatestWith(viewModel.items.producer) |> filter { index, items in index < items.count } |> map { index, items in items[index] }
Where Next?
elm-lang.or
gwww.erlan
g.orgfsharp.org
https://www.haskell.or
g
https://github.com/ReactiveCocoa/ReactiveCocoa
https://github.com/ReactiveX/RxSwift
https://github.com/typelift/Swiftz
https://github.com/
thoughtbot/Argo
www.objc.io/books/fpinswift/fsharpforfunandprofit.com/posts/recipe-part2/
adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
Where Next• Functors, Applicatives and Monads in Pictures
• Railway Oriented Programming
• Functional Programming in Swift (Objc.io)
• Argo
• Swiftz
• RxSwift
• ReactiveCocoa-3.0
• Haskell, F#, Erlang, Elm
Thanks!
Comments / Questions?
@aleks_voronova-voronov