Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What Haskell Taught Me About Writing Swift

What Haskell Taught Me About Writing Swift

The slides from my talk at the Swift Summit conference in London on March 21st, 2015

Abizer Nasir

March 21, 2015
Tweet

More Decks by Abizer Nasir

Other Decks in Programming

Transcript

  1. What Haskell Taught Me About Writing swift
    Abizer Nasir | @abizern | abizern.org
    Swi! Contractor - Shutl

    View Slide

  2. Why Haskell?

    View Slide

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

    View Slide

  4. Favour Immutability

    View Slide

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

    View Slide

  6. · Favour structs over classes where possible.
    · Avoid mutating structs.

    View Slide

  7. The Owl Problem

    View Slide

  8. View Slide

  9. · Map a function over a list
    · Write the rest of your marvellous program!

    View Slide

  10. Map, Filter and Reduce

    View Slide

  11. Using map, filter and reduce can lead to more
    expressive code
    let youngestManOver40 = population
    .filter { $0.age > 40 }
    .reduce(41) { min }

    View Slide

  12. The Type System Is Your
    Friend

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  16. 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 { ... }

    View Slide

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

    View Slide

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

    View Slide

  19. Keep code with side effects
    in one place

    View Slide

  20. module Main where
    import Control.Monad
    import Numeric
    factoryTimes :: Double -> Double -> [Double]
    factoryTimes c f = 0.0 : [ c / (2.0 + k * f) | k productionTimes :: Double -> Double -> [Double]
    productionTimes x f = [ x / (2.0 + k * f) | k 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 forM_ [1..read t :: Int] $ \i -> do
    [c, f, x] let result = solve c f x
    putStrLn $ concat ["Case #", show i, ": ", Numeric.showFFloat (Just 7) result ""]

    View Slide

  21. Functions All The Way
    Down

    View Slide

  22. · 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.

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  26. 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
    }

    View Slide

  27. 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
    }
    }

    View Slide

  28. Do We Still Need OOP?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  34. 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?

    View Slide

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

    View Slide

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

    View Slide

  37. Unfamiliarity Can be
    Mistaken For Complexity.

    View Slide

  38. 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.

    View Slide

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

    View Slide

  40. References

    View Slide

  41. View Slide