Last active
September 11, 2015 09:32
-
-
Save MatiMax/fde58b3545047191c533 to your computer and use it in GitHub Desktop.
Understanding the implementaition of Swift Optionals. Save the file and rename the extension to "playground" to run it in Xcode's Playground.
This file contains hidden or 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
/*: | |
Swift `Optional`s, as noted in Apple's documentation, are implemented as an enumeration with two cases: | |
* None | |
* Some | |
The question is how to declare a type-agnostic enumeration. The answer is simple: By using a generic. | |
*/ | |
enum Opt<T>: CustomStringConvertible, NilLiteralConvertible { | |
case None | |
case Some(T) | |
//: By conforming to the `CustomStringConvertible` protocol we can even show nice string representations to the user. | |
var description: String { | |
switch self { | |
case .None: | |
return "NIL" | |
case .Some(let value): | |
return String("Opt(\(value))") | |
} | |
} | |
/*: | |
By conforming to the `NilLiteralConvertible` protocol we can provide initializers that make the | |
usage of the Opt enumeration much simpler. With this we can do things like | |
`let i: Opt<Int> = nil` | |
`let j: Opt<Float> = .None` | |
`let k = Opt<Int>(42) | |
`let s = Opt("An optional String")` | |
*/ | |
init() { self = .None } | |
init(nilLiteral: ()) { self = .None } | |
init(_ value: T) { self = .Some(value) } | |
} | |
/*: | |
To make the usage of the `Opt` enumeration more usable in code we define two new operators for | |
declaring and unwrapping the optionals. The operators `¡` and `¿` behave identical or similar to their | |
counterparts. | |
*/ | |
postfix operator ¿ {} // ? equivalent | |
postfix operator ¡ {} // ! equivalent | |
//: Again, the unwrapping operator function uses a generic to work properly on all `Opt` types. | |
postfix func ¡<T> (opt: Opt<T>) -> T { | |
switch opt { | |
case .None: | |
fatalError("Runtime exception: Unwrapping a NIL-value.") | |
case .Some(let value): | |
return value | |
} | |
} | |
/*: | |
The `Opt` declaration operator does something similar to the `?` operator in Swift, but there is a | |
substantial lexical difference between them. In Swift the `?` is not an operator per se but instead | |
re-interprets the type declaration. This is why | |
`let i: Int? = 1` and | |
`let i = Optional<Int>.Some(1)` or `let i = Optional.Some(1)` or `let i = Optional(1)` | |
as well as | |
`let j: Int? = nil` and | |
`let j = Optional<Int>.None` | |
are the same. | |
*/ | |
postfix func ¿ <T>(opt: T) -> Opt<T> { | |
return Opt(opt) | |
} | |
//: We declare the equality comparison operator in order to make our `Opt` enumeration behave correctly. | |
func == <T>(lhs: Opt<T>, rhs: Opt<T>) -> Bool { | |
switch (lhs, rhs) { | |
case (.Some, .Some): | |
return true | |
case (.None, .None): | |
return true | |
default: | |
return false | |
} | |
} | |
let i1 = Opt.Some(7) | |
let i2 = Opt(8.0) | |
let i3 = "An optional string"¿ | |
let i4 = 9¿ | |
let j1 = Opt<Int>.None | |
let j2: Opt<Int> = .None | |
let j3: Opt<Int> = nil | |
/*: | |
Unfortunately, we cannot do something like | |
`let j4: Int¿ = nil` | |
So the best approximation is the following | |
*/ | |
var j4 = 0¿ ; j4 = .None ; /* or */ j4 = nil | |
let k1: Int? = 42 | |
let k2 = Optional.Some(43) | |
let k3 = Optional<Int>(44) | |
let l1 = Optional<Int>.None | |
let l2: Int? = .None | |
let l3: Int? = nil | |
print("i1 = \(i1)") | |
print("i2 = \(i2)") | |
print("i3 = \(i3)") | |
print("i4 = \(i4)") | |
print("j1 = \(j1)") | |
print("j2 = \(j2)") | |
print("j3 = \(j3)") | |
print("j4 = \(j4)") | |
print("k1 = \(k1)") | |
print("k2 = \(k2)") | |
print("k3 = \(k3)") | |
print("l1 = \(l1)") | |
print("l2 = \(l2)") | |
print("l3 = \(l3)") | |
print("i1¡ = \(i1¡)") | |
print("i2¡ = \(i2¡)") | |
print("i3¡ = \(i3¡)") | |
print("i4¡ = \(i4¡)") | |
print("j1¡ = " /*, j1¡ */, "*** This would result in a runtime exception. ***") | |
print("j2¡ = " /*, j2¡ */, "*** This would result in a runtime exception. ***") | |
print("j3¡ = " /*, j3¡ */, "*** This would result in a runtime exception. ***") | |
print("j4¡ = " /*, j4¡ */, "*** This would result in a runtime exception. ***") | |
print("k1! = \(k1!)") | |
print("k2! = \(k2!)") | |
print("k3! = \(k3!)") | |
print("l1! = " /*, l1! */, "*** This would result in a runtime exception. ***") | |
print("l2! = " /*, l2! */, "*** This would result in a runtime exception. ***") | |
print("l3! = " /*, l3! */, "*** This would result in a runtime exception. ***") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment