Swift Learning Lunch
Brandon Kase
Highlight
Swift is :)
Strongly typed
func allItemsMatch<C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>(a: C1, b: C2) { ... } // book
optionals
Swift is :(
(too) Imperative
Statements instead of expressions:
let x = if y { 4 } else { 5 } // Not supported
Contents
Pattern Matching
Immutability
Optionals
Sadness
Patterns
Keep Alert
Pattern Matching
Programming Language Theory
"Every fundamental construction of data has both an introduction rule and an elimination rule" - Me
theory screenshots from: Practical Foundations for Programming Languages by Bob Harper
Intro/Elim example -- Function
INtroduce a function
swift:
// INTRO
let sum = { $0 + $1 }
// or
let sum = { (x,y) in x + y }
// or
let sum = { (x: Int, y: Int) -> Int in x + y }
c:
int (*sum)(int x, int y)
theory:
Intro/elim example -- Function
eliminate a function
swift:
sum(1, 2) // = 3
c:
sum(1, 2) // = 3
Intro/elim example -- sum (enums)
introduce an enum
swift:
enum Person { case Man; case Woman } // define an enum
let m: Person = .Man // intro1
let w = Person.Woman // intro2
c:
typedef enum {MAN, WOMAN} Person;
Person m = MAN; // intro1
Person w = WOMAN; // intro2
INTRO/ELIM EXAMPLE -- SUM (ENUMS)
Eliminate an enum
swift:
switch (person) { case .Man:
println("I'm a man")
case .Woman:
println("I'm a woman") }
c:
if (person == MAN) { printf("I'm a man"); } else { printf("I'm a woman"); }
theory:
PATTERN MATCHING
why does it matter
Can't we just use branches (if/else)
Doing so leads to BOOLEAN BLINDNESS
(see http://existentialtype.wordpress.com/2011/03/15/boolean-blindness/)
boolean blindness
Suppose that I evaluate the expression e=e’ to test whether e and e’ are equal. I have in my hand a bit. The bit itself has no intrinsic meaning; I must associate a provenance with that bit in order to give it meaning. “This bit being true means that e and e’ are equal, whereas this other bit being false means that some other two expressions are not equal”
church numerals
swift:
enum ChurchNumeral { case Z; case S(ChurchNumeral); } // crashes :(
z = zero
S(z) = 1 + 0 = 1
S(S(z)) = 1 + 1 + 0 = 2
S(x) = 1 + x
CHUrch numerals
plus with boolean blindness
swift:
func plus(x: ChurchNumeral, y: ChurchNumeral) { if x == Z {
return y
} else {
return S(plus(x.predecessor(), y))
}
}
Do we have a way to represent "at least 1" with Church numerals?
CHURCH NUMERALS
PLUS WITH Pattern matching
swift:
func plus(x: ChurchNumeral, y: ChurchNumeral) { switch (x) {
case Z:
return y
case S(let x_):
return S(plus(x_, y))
}
}
We aren't blinded by a boolean!
Pattern matching
real life example where it helped
-
You need to store the URL for some image in an object
-
Sometimes you make the object when you have the real URL
- Sometimes you make the object when you only have the S3 key and bucket
swift:
var url: String?
var bucket: String?
var key: String?
func realUrl() -> String {
return url ? url : computeUrl(key, bucket)
}
PATTERN MATCHING
REAL LIFE EXAMPLE WHERE IT HELPED*
swift:
enum S3Info { // we either have BOTH the key and the bucket, OR the url
case BucketKey(String, String)
case URL(String)
}
@lazy var url: String = {
switch (self._s3Info) {
case .BucketKey(let bucket, let key):
// if we have BOTH the key and the bucket, use them now. No blindness
return computeOnBucketKey(bucket, key)
case .URL(let u):
return u
}
}()
Immutability
swift:
// Immutable
let arr = [1, 2, 3]
// Mutable
var arr = [1, 2, 3]
Immutable data works well with enums
It makes your life easier.
Just do it.
Optionals motivation
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. - Tony Hoare
Null (nil) sucks.
It (usually) means the absence of some piece of data
All objects can be null, but many times we KNOW some particular object can't be null, so we don't check
Because of this, when an object is null we may forget to check!
Optionals
swift:
var x: String = "hello" // x can change (var) but can NEVER be nil
// Intro var xOpt: String? = "hello" // y CAN be nil // Elimination if let v = xOpt { // v can NEVER be nil! } else { // xOpt was nil! }
Optionals
Why the first slide in this section?
swift:
let xOpt: String? = "hello"
if xOpt != nil {
println(xOpt!)
}
AHHH Boolean Blindness
Don't use the postfix "!" on a variable. Ever.
Don't use it. Don't EVER USE IT
optionals
Unfortunately, we have to interop with the unsafely typed Objective-C.
func arrayByAddingObject(_ anObject: AnyObject!) -> [AnyObject]!
(Most) everything in Cocoa is auto-typed to (AnyObject!). This means that the compiler auto-unboxes the optional will let you use the value without checking for nil.
In Swift, this crashes your app. Read the APIs and make sure you check for nil.
optionals
capital "o" optionals
swift:
let xOpt: String? = "hello" // regular optional
let yOpt: Optional<String> = .None
let zOpt: Optional<String> = .Some("hello") // capital "O" Optional
Capital "O" Optionals are actually explicit enums in the language.
They are (mostly) interchangeable! You can do lower-case "O" optional stuff on capital "O" Optionals and vice versa
optional
useful stuff I discovered
If you want an optional stored property of type T that can be "nil" when the initializer finishes:
var storedProp: Optional<String> = .None
BUT extract it with pattern matching when you use it in something that's auto converted to an Objective-C object otherwise the compiler will outputs bad assembly
Optionals
useful stuff that I discovered
swift:
// extending Optional puts methods on optional as well!
extension Optional {
func getOrElse(els: @auto_closure () -> T) -> T {
switch (self) {
case .None:
return els()
case .Some(let t):
return t
}
}
}
Optional
Useful stuff that I discovered
getOrElse (from Scala) is SUPER useful since Pattern matching is a statement and not an expression in swift
let xOpt: Int? = 10
let yOpt: Int? = nil
let z = xOpt.getOrElse(0) + yOpt.getOrElse(1)
var realX: Int = 0
var realY: Int = 1
if let x = xOpt {
realX = x
}
if let y = yOpt {
realY = y
}
let z = x + y
optional
useful stuff that I discovered
Look closely:
func getOrElse(els: @auto_closure () -> T) -> T
It's an @auto_closure
This means that getOrElse short circuits!
You can raise an exception in the els for debugging!
sadness -- dealing with crashes
Extract capital "O" Optionals via pattern before any Objective-C auto conversion occurs
Dictionary<String, Any> will crash at runtime when converted to NSDictionary. Use Dictionary<String, AnyObject>.
(AnyObject is any Objective-C object, Any is protocol<>)
Sadness -- dealing with crashes
Enums with more than one generic type don't work yet
enum Result<T,E> {
case Success(T)
case Error(E)
}
Recursive enums don't work yet
enum Tree<T> {
case Leaf(T)
case Node(Tree<T>, T, Tree<T>)
}
Sadness -- dealing with crashes
Xcode sucks right now
Disable auto-complete
Avoid using the debugger
Sometimes the syntax-highlighter/autocomplete/other engine crashes, but it will keep auto-restarting until it stops crashing
Sadness -- slow things
Avoid using @objc (makes your Swift type objc compatible)
Avoid runtime type checks (only works on @objc and real Objective-C types)
// runtime type check boolean
if x is NSString {
let s = x as NSString // boolean blindness!!
// do something
}
// runtime type check pattern match
switch (x) {
case x as NSString:
// do something
case x as NSInt:
// do something
default:
// do something
}
Sadness -- dont trust libraries
This one's obvious, but I fell for it
The language is so new that any reasonable library will have bugs. Additionally, every beta update the language makes breaking changes
patterns
singleton
class Dog {
class var sharedInstance: Dog {
struct Singleton {
static let instance = Dog()
}
return Singleton.instance
}
}
// Usage
Dog.sharedInstance
from http://stackoverflow.com/questions/24024549/dispatch-once-singleton-model-in-swift
patterns
lazy initialization
bad:
var library: ALAssetsLibrary? = nil
func doSomething() {
if library == nil {
library = ALAssetsLibrary(); library.something()
}
}
@lazy var library: ALAssetsLibrary = ALAssetsLibrary()
func doSomething() {
library.something() // initialized here magically
}
Keep Alert
Use a bridging header to import Objective-C code into swift
// Bridging Header
#import "shared/utils/UserModel.h"
...
For frameworks import in any file and per file needed
import CoreGraphics
import Foundation
Keep alert
Watch out for the closures auto-returning the last statement
It's usually good, but can be bad
I ran into:
failureBlock: { err in
dispatch_semaphore_signal(sema)
} // Compile Error -- dispatch_semaphore_signal returns Int, failureBlock needs an (NSError!) -> Void
to fix:
failureBlock: { err in
dispatch_semaphore_signal(sema)
return
}
Swift Learning Lunch
By bkase
Swift Learning Lunch
Immutability, Pattern Matching, and stuff to look out for
- 1,268