- Reference semantics
- Immutability
- Value semantics
- Value types in practice
- Mixing value types and reference types
- 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
- NSString, NSArray, NSDictionary all defensively copy. They conform to
- Copying helps, but it's never good enough. You still have bugs. What if we had immutable objects with reference semantics?
- 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
- can lead to awkard interfaces, i.e: cannot do
- 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
- Swift implementation
- 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.
-
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
theny==x
) - transitive (missed this example)
- reflexive (
-
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
- It seems like there's no actual benefit to using
-
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
- 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
- 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.
- When implementing
- References to mutable objects
- ex: 2 instances of
BezierPath
(custom struct) each referencing the sameUIBezierPath
- Implement copy-on-write
- Use the mutable reference type (carefully) to avoid tooo much copying
- ex: 2 instances of
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.