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

Correct Behavior Through Type Safety — Justin Spahr-Summers

1fa9cb8c7997c8c4d3d251fb5e41f749?s=47 Realm
June 12, 2015

Correct Behavior Through Type Safety — Justin Spahr-Summers

Swift has brought a much stronger type system to Cocoa development, which helps catch more bugs at compile-time, before they ship and affect real users. But it’s not enough to bring Objective-C patterns to Swift, sprinkle in a few types, and call it done. With a more powerful type system, we are now able to prevent many more invalid states from ever occurring, and can use new patterns for statically ensuring the correctness of our programs. This talk will explain how types can be used to ensure correctness, how to prevent the existence of invalid states, and why unit tests are not enough. Finally, we’ll dive into some specific approaches to correctness that will take advantage of Swift’s strong type system.

This talk was presented at AltConf in June 2015.

1fa9cb8c7997c8c4d3d251fb5e41f749?s=128

Realm

June 12, 2015
Tweet

Transcript

  1. CORRECT BEHAVIOR THROUGH TYPE SAFETY

  2. @JSPAHRSUMMERS REACTIVECOCOA MANTLE CARTHAGE

  3. None
  4. TYPES PREVENT ERRORS (by restricting what you can do)

  5. NSArray IS MORE RESTRICTED THAN NSMutableArray

  6. NSArray PREVENTS MORE ERRORS THAN NSMutableArray

  7. Optional<T> RESTRICTS ACCESS TO ITS VALUE PREVENTS MISTAKES WITH nil

  8. Array<T> RESTRICTS INSERTIONS PREVENTS CRASHES AFTER RETRIEVAL

  9. More generally…

  10. TYPES = PROOFS (the Curry–Howard correspondence)

  11. A → B

  12. A → B func toInteger(num: Double) -> Int { let

    result = round(num) return Int(result) }
  13. STRING → STRING

  14. STRING → STRING func identity(s: String) -> String { return

    s }
  15. WE CAN USE TYPES TO PROVE CORRECTNESS

  16. WHAT DOES NSDATA PROVE? (Not much.)

  17. WHAT DOES NSSTRING PROVE? Characters, not just bytes!

  18. WHAT DOES NSURL PROVE? Valid URL, not just a string!

  19. EXTENDS TO ANY KIND OF VALIDATION

  20. What about tests?

  21. Science break!

  22. TESTS ARE SUPPORT TYPES ARE PROOF

  23. Caveats ▸ Proofs can contain errors too ▸ Types can’t

    represent everything ▸ Tests may be good enough
  24. None
  25. PHANTOM TYPES1 struct FileHandle<A> { let handle: NSFileHandle } enum

    Read {} enum Write {} func openForReading(path: String) -> FileHandle<Read> func openForWriting(path: String) -> FileHandle<Write> func readDataToEndOfFile(handle: FileHandle<Read>) -> NSData 1 See functional snippet #13 from objc.io.
  26. Case study: ERRORS IN REACTIVECOCOA

  27. SIGNALS CONSIST OF… ▸ Any number of next events ▸

    Optionally an error or completed event Next* (Error | Completed)?
  28. RACSignal *

  29. None
  30. PROPERTY BINDING IN RAC 2 RAC(self.imageView, image) = [RACObserve(self, model)

    flattenMap:^ RACSignal * (Model *model) { return [model fetchImage]; }];
  31. PROPERTY BINDING IN RAC 2 RAC(self.imageView, image) = [RACObserve(self, model)

    flattenMap:^ RACSignal * (Model *model) { return [model fetchImage]; }]; *** Received error from RACSignal in binding for key path "image" on UIImageView: Error Domain=NSURLErrorDomain Code=-1 "Operation could not be completed."
  32. Solution: TYPES!

  33. Signal<T, Error>

  34. enum NoError {}

  35. PROPERTY BINDING IN RAC 3 func <~ <T> (property: MutableProperty<T>,

    signal: Signal<T, NoError>) func <~ <T> (property: MutableProperty<T>, producer: SignalProducer<T, NoError>)
  36. PROPERTY BINDING IN RAC 3 class Model { func fetchImage()

    -> SignalProducer<UIImage, NSError> } self.imageProperty <~ self.modelProperty.producer // TYPE ERROR: NSError is incompatible with NoError |> flatMap(.Latest) { model in return model.fetchImage() }
  37. PROPERTY BINDING IN RAC 3 class Model { func fetchImage()

    -> SignalProducer<UIImage, NSError> } self.imageProperty <~ self.modelProperty.producer |> flatMap(.Latest) { model in return model.fetchImage() // Ignore any error event |> catch { error in .empty } }
  38. TYPED ERRORS VS. throws

  39. TYPES CAN ALSO DESCRIBE EFFECTS

  40. IO MONAD IN HASKELL

  41. WITH IO, YOU CAN… ▸ Put a value into it

    ▸ Perform side effects using the value ▸ “Lift” pure functions to apply to the value ▸ Never2 get the result back out 2 Except through the rarely-used unsafePerformIO.
  42. WITH A SIGNAL, YOU CAN… ▸ Put values into it

    ▸ Perform time-based operations using the values ▸ “Lift” pure functions to apply to the values ▸ Register for delivery3 to get the results back out 3 It’s possible to synchronously wait for results, but the framework highly discourages this.
  43. Example: TYPES FOR CONCURRENCY

  44. THE UI PROBLEM™ dispatch_async(someBackgroundQueue) { // Oops! self.textField.text = "foobar"

    }
  45. CAPTURING UI CODE IN THE TYPE SYSTEM struct UIAction<T> {

    init(_ action: () -> T) func enqueue() func enqueue(handler: T -> ()) func map<U>(f: T -> U) -> UIAction<U> func flatMap<U>(f: T -> UIAction<U>) -> UIAction<U> }
  46. WHAT IF…? extension UITextField { /// Sets the receiver's text

    to the given string /// when the returned action is executed. func jss_setText(text: String) -> UIAction<()> }
  47. SLIDES AND NOTES Available at git.io/vkjl9 (github.com/jspahrsummers/correct-behavior-through-type-safety) THANKS TO… Neil

    Pankey, Rob Rix, James Lawton, Gordon Fontenot, Eli Perkins, Javi Soto, anyone else who I omitted in error, and you!