Skip to content

Instantly share code, notes, and snippets.

@MatiMax
Last active September 11, 2015 09:32
Show Gist options
  • Save MatiMax/fde58b3545047191c533 to your computer and use it in GitHub Desktop.
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.
/*:
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