Slide 1

Slide 1 text

WHAT HASKELL TEACHES ME ABOUT WRITING SWIFT Abizer Nasir | @abizern | abizern.org Swift Contractor - Shutl

Slide 2

Slide 2 text

TL;DL LEARNING HASKELL WILL MAKE YOU A BETTER SWIFT PROGRAMMER.

Slide 3

Slide 3 text

WHY HASKELL?

Slide 4

Slide 4 text

A polymorphically statically typed, lazy, purely functional language. — The Haskell Wiki

Slide 5

Slide 5 text

TYPE INFERENCE

Slide 6

Slide 6 text

summer :: Int -> Int -> Int summer a b = a + b λ> :t summer summer :: Int -> Int -> Int

Slide 7

Slide 7 text

--summer :: Int -> Int -> Int summer a b = a + b λ> :t summer summer :: Num a => a -> a -> a

Slide 8

Slide 8 text

λ> :i + class Num a where (+) :: a -> a -> a ... -- Defined in ‘GHC.Num’ infixl 6 +

Slide 9

Slide 9 text

FAVOUR IMMUTABLE STATE

Slide 10

Slide 10 text

▸ Values don’t change under you. ▸ Easier to maintain the code. ▸ More likely to be thread safe.

Slide 11

Slide 11 text

▸ Prefer let to var ▸ Prefer structs over classes where possible. ▸ Avoid mutating structs.

Slide 12

Slide 12 text

LISTS

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

▸ Map a function over a list ▸ Write the rest of your marvellous program!

Slide 15

Slide 15 text

Don’t iterate over a list

Slide 16

Slide 16 text

HIGHER ORDER FUNCTIONS let list = [1, 2, 3, 4] let squares = list.map{$0 * $0} let evens = list.filter{$0 % 2 == 0} let sum = list.reduce(0, combine: +)

Slide 17

Slide 17 text

REDUCE IS OFTEN ENOUGH let squares2 = list.reduce([Int]()) { var accum = $0 accum.append($1 * $1) return accum } let evens2 = list.reduce([Int]()){ if $1 % 2 == 0 { var accum = $0 accum.append($1) return accum } else { return $0 } }

Slide 18

Slide 18 text

MAPPING AND FILTERING IS MORE EXPRESSIVE let sumOfSquaresOfEvenNumbers = list .filter{ $0 % 2 == 0 } .map { $0 * $0 } .reduce(0, combine: +)

Slide 19

Slide 19 text

MONADS

Slide 20

Slide 20 text

A monad is a monoid in the category of endofunctors, what’s the problem? -- Philip Wadler1 1 Not really – see http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html

Slide 21

Slide 21 text

A MONAD IS (SORT OF) LIKE A BURRITO

Slide 22

Slide 22 text

A VALUE IN A CONTEXT

Slide 23

Slide 23 text

▸ Optional is a context that contains a value or the lack of a value

Slide 24

Slide 24 text

▸ A list is a context that contains values in an order

Slide 25

Slide 25 text

▸ Result is the context that has a value or an error in getting that value

Slide 26

Slide 26 text

THE TYPE SYSTEM IS YOUR FRIEND

Slide 27

Slide 27 text

There is a whole class of bugs we no longer have to worry about.

Slide 28

Slide 28 text

"If it compiles, you're most of the way to your solution"

Slide 29

Slide 29 text

typealias is useful for making code clearer. func decode(json: [String : AnyObject]) -> MyType { ... } or typealias JSONDictionary = [String : AnyObject] func decode(json: JSONDictionary) -> myType { ... }

Slide 30

Slide 30 text

It can make higher order function declarations easier to read. // Taken from AlamoFire typealias JSONResponseHandler = (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> () // Define a common completion block with specific handler func responseHandlerWithCompletion(completion: Result) -> JSONResponseHandler { ... }

Slide 31

Slide 31 text

▸ You can reason about your problem in terms of the types and transformations of those types, before writing any code.

Slide 32

Slide 32 text

TYPE DRIVEN DEVELOPMENT

Slide 33

Slide 33 text

func shuffle(deck: Deck) -> Deck { ... } func deal(deck: Deck, toPlayers: [Player]) -> [Hand] { ... } func winner(hands: [Hand]) -> Player { ... }

Slide 34

Slide 34 text

KEEP CODE WITH SIDE EFFECTS IN ONE PLACE

Slide 35

Slide 35 text

module Main where import Control.Monad import Numeric factoryTimes :: Double -> Double -> [Double] factoryTimes c f = 0.0 : [ c / (2.0 + k * f) | k <- [0.0, 1.0 ..]] productionTimes :: Double -> Double -> [Double] productionTimes x f = [ x / (2.0 + k * f) | k <- [0.0, 1.0 ..]] times :: Double -> Double -> Double -> [Double] times c f x = zipWith (+) production factory where production = productionTimes x f factory = scanl1 (+) $ factoryTimes c f firstMinimum :: [Double] -> Double firstMinimum (x:y:ys) = if x < y then x else firstMinimum (y:ys) solve :: Double -> Double -> Double -> Double solve c f x = firstMinimum $ times c f x main :: IO () main = do t <- getLine forM_ [1..read t :: Int] $ \i -> do [c, f, x] <- fmap (map read . words) getLine let result = solve c f x putStrLn $ concat ["Case #", show i, ": ", Numeric.showFFloat (Just 7) result ""]

Slide 36

Slide 36 text

FUNCTIONS ALL THE WAY DOWN

Slide 37

Slide 37 text

▸ Don't have state, have functions that return values; although this is easier said than done. ▸ Higher order functions can be used to reduce duplication in code.

Slide 38

Slide 38 text

▸ Aim to write small, pure functions with no side effects. ▸ Avoid mixing pure and impure code. ▸ Aim to write small, obviously correct functions.

Slide 39

Slide 39 text

Composition infix operator >>> { associativity right precedence 170 } func >>> (f: B -> C, g: A -> B) -> A -> C { return { x in f(g(x)) } }

Slide 40

Slide 40 text

Map on Optionals infix operator <^> { associativity left } func <^> (f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return nil } }

Slide 41

Slide 41 text

Applicative on Optionals infix operator <*> { associativity left } func <*> (lhs: (A -> B)?, rhs: A?) -> B? { if let f = lhs { if let x = rhs { return f(x) } } return nil }

Slide 42

Slide 42 text

Bind on Optionals infix operator >>== { associativity left precedence 150 } func >>== (a:A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return nil } }

Slide 43

Slide 43 text

DO WE STILL NEED OOP?

Slide 44

Slide 44 text

OOP concerns data objects and operations on those data objects — My naive definition

Slide 45

Slide 45 text

"Why are methods of protocols defined outside the Type?" struct SomeType: Equatable { ... } func ==(lhs: ...

Slide 46

Slide 46 text

If we are supposed to favour immutability, shouldn't we be moving away from classes?

Slide 47

Slide 47 text

Have you ever created a class with nothing but class functions just to keep related code together?

Slide 48

Slide 48 text

Have you ever carefully exposed only the methods that were safe to call on your objects, keeping other methods private?

Slide 49

Slide 49 text

If you have pure functions (no side effects, same inputs, same outputs) is it worth worrying about their access modifiers? Can you make them bare functions instead?

Slide 50

Slide 50 text

If the functions can be grouped together into a logical set of functionality, could a protocol be defined to encompass that functionality instead?

Slide 51

Slide 51 text

Programming to an interface was a recommendation even in the times of Objective-C. The stronger type system in Swift makes it even more attractive.

Slide 52

Slide 52 text

REFERENCES

Slide 53

Slide 53 text

Learn You a Haskell For A Great Good http://learnyouahaskell.com

Slide 54

Slide 54 text

Real World Haskell http://book.realworldhaskell.org

Slide 55

Slide 55 text

Functional Programming in Swift http://www.objc.io/books/fpinswift/

Slide 56

Slide 56 text

Maybe Haskell https://gumroad.com/l/maybe-haskell/

Slide 57

Slide 57 text

LEARNING HASKELL WILL MAKE YOU A BETTER SWIFT PROGRAMMER

Slide 58

Slide 58 text

LEARNING A PROGRAMMING LANGUAGE WILL MAKE YOU A BETTER PROGRAMMER

Slide 59

Slide 59 text

WITH COCOA WE HAVE THE CHOICE OF TWO DIFFERENT APPROACHES TO THE SAME PROBLEM SPACE.

Slide 60

Slide 60 text

UNFAMILIARITY CAN BE MISTAKEN FOR COMPLEXITY

Slide 61

Slide 61 text

I want to write code like this: static func decode(object: JSON) -> DateWithTimeZone? { return jsonDictionary(object) >>== { dict in DateWithTimeZone.create <^> dict[dateWithTimeFormatKey(.Date)] >>== jsonDate <*> dict[dateWithTimeFormatKey(.TimeZone)] >>== jsonTimeZone } } It looks complex because it's unfamiliar, but it's clearer than having a pyramid of doom.

Slide 62

Slide 62 text

This stuff is hard. But compared with what we already do and what we aspire to do; it's perfectly within our grasp.

Slide 63

Slide 63 text

THANK YOU & HAVE A GREAT WEEK! ABIZER NASIR | @ABIZERN | ABIZERN.ORG