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

A Neatly Typed Message

A Neatly Typed Message

The talk is about the readability of Swift code. I'll treat it not as a well-defined goal to achieve, but as a spectrum that you need to decide where to land on. Looking at the variations of popular Cocoa patterns and Swift language constructs we'll identify their readability tradeoffs and chances for improvement. We'll also learn some fine techniques to widen readability spectrum using the power of the Swift type system.

43d2bef703ec7165166f161f137ac54f?s=128

Krzysztof Siejkowski

March 03, 2017
Tweet

Transcript

  1. A Neatly Typed Message Krzysztof Siejkowski

  2. None
  3. What’s our biggest problem? “Maintaining Mental Models: A Study of

    Developer Work Habits”
  4. Understanding the rationale behind a piece of code

  5. Understanding the rationale behind a piece of code Switching tasks

    because of requests from my teammates
  6. “Story of my life”

  7. Readability is crucial for understanding

  8. programs must be written for people to read, and only

    incidentally for machines to execute — Structure and Interpretation of Computer Programs book
  9. the essential material (are) techniques used to control the intellectual

    complexity of software systems. — Structure and Interpretation of Computer Programs book
  10. Complexity • intrinsic • incidental

  11. Readability quest [Press start]

  12. World 1:
 API design

  13. func split(email: String) -> (String, String)? { let components =

    email.components(separatedBy: "@") guard components.count == 2, let username = components.first, let domain = components.last, !username.isEmpty && !domain.isEmpty else { return nil } return (username, domain) } let splitEmail = split(email: ”trySwift@tokyo.jp")
  14. func split(email: String) -> (String, String)? { let components =

    email.components(separatedBy: "@") guard components.count == 2, let username = components.first, let domain = components.last, !username.isEmpty && !domain.isEmpty else { return nil } return (username, domain) } let splitEmail = split(email: ”trySwift@tokyo.jp")
  15. func split(email: String) -> (String, String)? { let components =

    email.components(separatedBy: "@") guard components.count == 2, let username = components.first, let domain = components.last, !username.isEmpty && !domain.isEmpty else { return nil } return (username, domain) } let splitEmail = split(email: ”trySwift@tokyo.jp")
  16. func split(email: String) -> (String, String)?

  17. Level 1-1: comments /// Splits the email into username and

    domain. /// - Parameter email: A string that possibly /// contains the email address value. /// - Returns: Either: /// - Tuple with username as first element /// and domain as second /// - Nil when passed argument cannot be split func split(email: String) -> (String, String)?
  18. Level 1-1: comments /// Splits the email into username and

    domain. /// - Parameter email: A string that possibly /// contains the email address value. /// - Returns: Either: /// - Tuple with username as first element /// and domain as second /// - Nil when passed argument cannot be split func split(email: String) -> (String, String)?
  19. Level 1-2: symbols typealias SplitEmail = (username: String, domain: String)

    struct SplitEmail { let username: String let domain: String } func split(email: String) -> SplitEmail?
  20. Costs of symbols • fixed discoverability • marginal recognition

  21. Either a lot
 or not at all

  22. Level 1-2: symbols func split(email: String) -> SplitEmail ?

  23. Level 1-3: wrappers enum SplitEmail { case valid(username: String, domain:

    String) case invalid init(from email: String) { let components = email.components(separatedBy: "@") if /* same checks as before */ { self = .valid(username: username, domain: domain) } else { self = .invalid }
  24. Level 1-3: wrappers func split(email: String) -> SplitEmail

  25. Level 1-4: concepts protocol Splitter { associatedtype Splitting associatedtype Splitted

    func split(value: Splitting) -> (Splitted, Splitted)? } struct EmailSplitter: Splitter { func split(value: String) -> (String, String)? { let components = value.components(separatedBy: "@") guard /* same checks as before */ else { return nil } return (first, second) }
  26. Level 1-4: concepts enum Split<Value, S: Splitter> where S.Splitting ==

    Value, S.Splitted == Value { case splitted(Value, Value) case invalid init(_ value: Value, using splitter: S) { if let (first, second) = splitter.split(value: value) { self = .splitted(first, second) } else { self = .invalid } See https://github.com/Ben-G/Validated/ by Benjamin Encz
  27. Level 1-4: concepts func split(email: String) -> Split<String, EmailSplitter> //

    result: Split // value: String // transform: EmailSplitter
  28. Is complexity justified?

  29. End of World 1

  30. World 2:
 Delegation

  31. protocol Delegate: class { func foo() } struct Announcing {

    open weak var delegate: Delegate? } var announcing = Announcing() announcing.delegate = delegate 
  32. Conventions are HP for readability

  33. Level 2-1: Initializer injection struct Announcing { private weak var

    delegate: Delegate? init(to delegate: Delegate) { self.delegate = delegate } } let announcing = Announcing(to: delegate)
  34. Level 2-2: Weak closure struct Announcing { private let delegate:

    () -> Delegate? init(to delegate: @escaping () -> Delegate?) { … } } let announcing = Announcing(to: { [weak delegate] in delegate })
  35. Level 2-3: Weak function func weak<T: AnyObject>(_ object: T) ->

    () -> T? { return { [weak object] in object } } struct Announcing { private let delegate: () -> Delegate? init(to delegate: @escaping () -> Delegate?) { … } } let announcing = Announcing(to: weak(delegate))
  36. Level 2-4: Weak wrapper struct Weak<T: AnyObject> { private let

    internalValue: () -> T? init(_ value: T) { internalValue = { [weak value] in value } } } struct Announcing { private let delegate: Weak<Delegate> init(to delegate: Weak<Delegate>) { ... } }
  37. Readability is contextual

  38. There is no castle
 with a princess inside

  39. Empathy is the god mode

  40. Who are we 
 writing for?

  41. No such thing as the readability

  42. How do we win?

  43. May 
 the feedback
 be with you

  44. Thank you! @_siejkowski siejkowski.net