Skip to content

Instantly share code, notes, and snippets.

@rbobbins
Created June 12, 2015 22:09
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save rbobbins/596bb6896141dca5934d to your computer and use it in GitHub Desktop.
Save rbobbins/596bb6896141dca5934d to your computer and use it in GitHub Desktop.
Build Better Apps with Value Types in Swift

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment