Created
June 10, 2014 12:44
-
-
Save Vaguery/83088b27c557dfbb0617 to your computer and use it in GitHub Desktop.
Wrestling with Swift identity functions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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