Skip to content

Instantly share code, notes, and snippets.

@Vaguery
Created June 10, 2014 12:44
Show Gist options
  • Save Vaguery/83088b27c557dfbb0617 to your computer and use it in GitHub Desktop.
Save Vaguery/83088b27c557dfbb0617 to your computer and use it in GitHub Desktop.
Wrestling with Swift identity functions
import Cocoa
// This is a Swift Playground to explore some unexpected behavior of '==='.
//
// ICYMI, '===' is the test of _identity_ in Swift; two values a,b are
// references to the same object if a === b
//
// My concern with this is the necessity of testing eqality and identity when
// manipulating complex graphs constructed automatically by code.
//
// Swift's Enumerations are very powerful, and my concern arose because I was
// storing full-fledged objects as associated values in relatively simple enum
// "wrappers". You'll see something similar below when I store Array values
// in the associated data of enum vars. The point of all this is that I MIGHT
// be storing Arrays of values of that very Enumeration, building a tree or
// graph.
//
// And in that situation, I want to be certain---using unit tests---that the
// values are stored by value, not by reference.
//
// So the goal here is to find a way to be ABLE to test (using a true/false ssertion
// of some kind) whether two variables set to an enum-with-associated-data are
// referring to IDENTICAL data or hold COPIES of the value.
//
// Turns out to be interesting....
// I start by exploring '==' and '===' for Strings, which are absolutely,
// definitely Swift value types: copied on creation.
var x = "foo"
var y = "foo"
var z = x
var w = x
x == y // true
//x === y // fails, suggesting override of '==='
x == z //
//x === z // fails, suggesting override of '==='
//w === z // fails, suggesting override of '==='
// let's do one little thing to check though...
x += "bar" // "foobar"
z // "foo" YAY! THE SYSTEM WORKS!
// OK now a quick look at how '===' is supposed to work:
let obj1 = NSSpellServer() // a proper object
let obj2 = NSSpellServer() // kinda the same thing
obj1 == obj2 // false
obj1 === obj2 // false
obj1 === obj1 // true (just checking!)
// Now then, if one is building a tree or graph, there need to be Arrays:
var a1:Int[] = [1,2,3]
var a2:Int[] = [1,2,3]
a1 == a1 // true; whew!
a1 == a2 // also true; we seem to be OK
a1 === a2 // false, as expected
a1 === a1 // ...false...?
// DAGNABBIT
// OK, so in my case I was concerned about the associated data of enum-typed instances:
enum Zork {
case Grue(Int[])
case Dragon(Int[])
}
var grue1 = Zork.Grue(a1)
var grue2 = Zork.Grue(a1)
var drag1 = Zork.Dragon(a1)
// aside: one way to fetch associated values
func grueGetter(someE:Zork) -> Int[] {
switch someE {
case .Grue(let ints):
return ints
default:
return Int[]()
}
}
// OK let's see whether I can mess up the associated data
let gotSome = grueGetter(grue1) // [1,2,3], which looks like a1, just as expected
a1 += 7 // a1 is now [1,2,3,7]
let gotSomeMore = grueGetter(grue1) // [1,2,3]
// Whew! grue1's associated values did not in fact change.
// OK, now let's get back to equality and identity...
// grue1 == grue2 // FAILS, because '==' is not defined for Enumerations
// And that's why I spent yesterday comparing the associated data of enum values.
// HERE, LET ME SHOW YOU WHY IT TOOK ALL DAY:
grueGetter(grue1) == grueGetter(grue2) // true, because the VALUES are the same
grueGetter(grue1) === grueGetter(grue2) // true... HANG ON!?
a1 === a1 // This is still false!
// DAGNABBIT
// And just to drive it home...
grue1 = Zork.Grue(a1) // make a fresh one...
grueGetter(grue1) == grueGetter(grue2) // false
grueGetter(grue1) === grueGetter(grue2) // false
grueGetter(grue1) === grueGetter(grue1) // true
grueGetter(grue1) === a1 // FALSE
grueGetter(grue1).count // just to make sure: 4
a1.count // also 4
grue2 = Zork.Grue(a1) // let's build that one a fresh friend...
grueGetter(grue1) == grueGetter(grue2) // TRUE
grueGetter(grue1) === grueGetter(grue2) // TRUE ??!?
grueGetter(grue1) === grueGetter(grue1) // TRUE ???????...
grueGetter(grue1) === a1 // FALSE :/
// and for the final moment of Zen...
a1 === a1 // FALSE
// DAGNABBIT!!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment