Skip to content

Instantly share code, notes, and snippets.

@dev-xdyang
Forked from rbobbins/value_types.md
Created June 24, 2016 08:11
Show Gist options
  • Select an option

  • Save dev-xdyang/fddf92a864aeadd7b3024346f786c8b5 to your computer and use it in GitHub Desktop.

Select an option

Save dev-xdyang/fddf92a864aeadd7b3024346f786c8b5 to your computer and use it in GitHub Desktop.

Revisions

  1. @rbobbins rbobbins created this gist Jun 12, 2015.
    127 changes: 127 additions & 0 deletions value_types.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,127 @@
    # Build Better Apps with Value Types in Swift

    ## Agenda
    * Reference semantics
    * Immutability
    * Value semantics
    * Value types in practice
    * Mixing value types and reference types

    ## Reference semantics
    * Classes are reference types. E.g a `Temperature` class with farenheit property
    * When you want to prevent sharing, you manually copy that object.
    * Defensive copying
    * Copying is all over cocoa touch and objective-c
    * NSString, NSArray, NSDictionary all defensively copy. They conform to `NSCopying`
    * e.g - NSDictionary calls `-copy` on its keys
    * Copying helps, but it's never good enough. You still have bugs. What if we had immutable objects with reference semantics?

    ### Immutability
    * Eliminates many problems caused by reference semantics w/ mutation
    * You can't have unintended side effects
    * Some downsides:
    * can lead to awkard interfaces, i.e: cannot do `temperature.farenhiet += 10.0`
    * does not map efficiently to the machine model
    * Example: Sieve of Eratosthenes
    * Swift implementation
    * Entirely relies on mutation
    * Here's a "beautiful" Haskell implementation
    * Paper by Melisa O'Neill called "The Genuine Seive of Eratosthenes" - shows that the haskel implementation isn't as performant as people thing it is
    * Immutability in cocoa/cocoa touch
    * e.g NSDate, NSURL, UIImage, NSNumber, etc.
    * Improved safety, since there's no need to use copy
    * Inefficient - i.e. what if you want to build up a URL from a bunch of components?
    * In conclusion, you can't go totally without immutability or you'd be crazy.

    ### Value Semantics
    * Easy to use, straightforward
    * Mutating one variable of some value type will never affect a different variable
    * We already use it for small types, ie Integer, `CGPoint`. Swift extends this behavior to more complex types.
    * In Swift, dicitonaries, arrays, strings, etc. are all values types.
    * Value types are composable
    * Equality is established by the value of a variable. Not by its identity. Not by how we arrived at the value
    * *Recommendation*: All your value types should implement `Equatable`
    * `==` must be:
    * reflexive (`x==x` is `true)
    * symetric (if `x==y` then `y==x`)
    * transitive (missed this example)
    * Defining a value type with `let` will prevent you from chaing its properties
    * It seems like there's no actual benefit to using `let` when creating a value type

    * Works beautifully in swift.
    * `let` means "the value will never change"
    * `var` means you can update the value without affecting any other values
    * Value types means freedom from race conditions! #blessed
    * Performance: What about all those copies? Isn't that lowering performance?
    * Nope, copying is cheap in swift. It's constant time. for low level fundamental types and structs, enums or tuples of value types
    * Extensible data structures use copy-on-write. I.e for string, dictionaries, arrays.
    * They're not copied until necessary

    ### Value Types in Practice
    * Example: Build a diagram made of a circle and a polygone
    ```
    struct Circle: Equatable {
    var center: CGPoint
    var radius: Double
    ... (missed the init method)
    }
    struct Polygon: Equatable {
    var corners: [CGPoint] = []
    }
    //pt 2
    protocol Drawable {
    func draw()
    }
    extension Polygon: Drawable {
    func draw() { //Draw via core graphics }
    }
    extension Circle: Drawable {
    func draw() { //Draw via core graphics }
    }
    //pt3
    struct Diagram: Drawable {
    var items: [Drawable] = []
    }
    var doc = Diagram()
    doc.addItem(Polygon())
    doc.addItem(Circle())
    doc.addItem(Diagram())
    doc.addItem(doc) // This would infinitely recurse if we were using reference semantics. But actually, it's a completely separate instance being added to the array, so it's fine. No problem here.
    //missed something here
    ```

    ### Mixing value types and reference types
    * A value type *can* contain a reference
    * Copies of the value type will share the reference
    * How is equality affected by this?
    * When implementing `==`, you have to use isEqual for reference types.
    * References to mutable objects
    * ex: 2 instances of `BezierPath` (custom struct) each referencing the same `UIBezierPath`
    * Implement copy-on-write
    * Use the mutable reference type (carefully) to avoid tooo much copying

    ### Impleenting Undo using value types
    ```
    var doc = Diagram()
    var undoStack: [Diagram] = []
    undoStack.append(doc)
    doc.addItem(Polygon())
    undoStack.append(doc)
    doc.addItem(Circle())
    undoStack.append(doc)
    ```
    * Every action results in a doc instance
    * efficient because of copy on write
    * This is the technique used by photoshop
    * It slices your photo into tiles represented by value types
    * The whole document is represented by a collection of tiles.