Swift Learning Lunch

Brandon Kase


Highlight

Swift is :)


Strongly typed

algebraic data types (enums / pattern matching)

generics with constraints
 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
theory:

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
theory:

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 :(

theory:

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))   } }
if x is NOT zero it must be at least 1

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))   } }
Now we don't have to recompute the predecessor!
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)
  }
(bad version)

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
   }
 }()
(good version)*not actually in code anymore, due to refactor, but I thought it was a good example

Immutability


swift:
 // Immutable
 let arr = [1, 2, 3]

 // Mutable
 var arr = [1, 2, 3]

Use "let" whenever you can.

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
Lazy. Immutable. Perfect.
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()
   }
 }
good:
 @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