Skip to content

Instantly share code, notes, and snippets.

@0thernet
Created June 6, 2014 23:46
Show Gist options
  • Save 0thernet/2de7547a1f1f01e26a2b to your computer and use it in GitHub Desktop.
Save 0thernet/2de7547a1f1f01e26a2b to your computer and use it in GitHub Desktop.

WWDC 2014

Extending Swift

Making something anonymous

for (key, _) in dictionary {
	println(key)
}

Simple protocols

protocol Pullable {
	func pull()
}

Adopting a Protocol

class Boards: Thing, Pullable {
	func pull () {
		// ...
	}
}
  • Use a protocol value with a conditional cast
    • as Pullable

Special Protocols

  • LogicValue, Printable, Sequence, IntegerLiteralConvertible, FloatLiteralConvertible, StringLiteralConvertible, ArrayLiteralConvertible, DictionaryLiteralConvertible
protocol Printable {
	var description: String { get }
}

Overloading an operator

// operator declaration
operator infix ~ {} 

func ~ (decorator: (Thing) -> String, object: Thing) -> String {
	return decorator(object)
}

fun an(object: Thing) -> String {
	return object.nameWithArticle
}

println("You want \(an ~ object).")

Subscripts

extension Place {
	subscript (direction: Direction) -> Place? {
		get {
			return exits[direction]
		}
		set(destination: Place?) {
			exits[direction] = destination
		}
	}
}

Generics

  • Any is the empty Protocol type
    • but protocol types throws away type information
      • great for dynamic polymorphism
    • Generics conserve type information
      • great for type safety
        • no unsafe downcasts
      • great for performance
        • compiler can generate better code
  • Anything in Swift can be printed
func peek<T>(interestingValue: T) -> T {
	//...
}

Type Relationships

func swap<T>(inout x: T, inout y: T) {
	let tmp = x
	x = y
	y = tmp
}

Finding an item in an array

func indexOf<T: Equatable>(sought: T, inArray array: T[]) -> Int? {
	for i in 0..array.count {
		if array[i] == sought {
			return i
		}
	}
	return nil
}

Inside Equatable

protocol Equatable {
	func == (lhs: Self, rhs: Self) -> bool
}

func != <T: Equatable>(lhs: Self, rhs: Self) -> bool {
	return !(lhs == rhs)
}
  • Protocols preserve type information
  • Some languages throw away type information in generics
  • operator requirements can be specified outside the type body

Example: Approximating phi

Naive

func fib(n: Int) -> Double {
	return n < 2 ? Double(n) : fib(n-1) + fib(n-2)
}

let phi = fib(45)/fib(44)	// 11 seconds

Memoized

var fibMemo = Dictionary<Int, Double>()

func fib(n: Int) -> Double {
	if let result = fibMemo[n] {
		return result
	}
	let result = n < 2 ? Double(n) : fib(n-1) + fib(n-2)
	fibMemo[n] = result
	return result
}

let phi = fib(45)/fib(44)	// 0.1 seconds = 100x faster

Automatic memoization

let fib: (int) -> Double = memoize {
	fib, n in 
	n > 2 ? Double(n) : fib(n-1) + fib(n-2)
}

let fib = memoize {
	fib, n in 
	n > 2 ? Double(n) : fib(n-1) + fib(n-2)
}

Take One

func memoize<T: Hashable, U>( body: (T)->U ) -> (T) -> U {
	var memo = Dictionary<T, U>()
	return { x in
		if let q = memo[x] { return q}
		let r = body(x)
		memo[x] = r
		return r
	}
}

let factorial = memoize { x in x == 0 ? 1 : x * factorial(x-1) } // doesn't work
  • doesn't work for recursive functions

Take Two

func memoize<T: Hashable, U>( body: ((T)->U, T)->U ) -> (T) -> U {
	var memo = Dictionary<T, U>()
	var result: ((T)->U)! // implicitly unwrapped optional
	result = { x in
		if let q = memo[x] { return q}
		let r = body(result, x)
		memo[x] = r
		return r
	}
	return result
}

let factorial = memoize { factorial, x in x == 0 ? 1 : x * factorial(x-1) }

Synergy of powerful features

  • Type deduction for concision
  • Trailing closure syntax for expressivity
  • Truly generic functions for safety and performance

Generic Types

struct Stack<T> {
	mutating fun push(x: T) {
		items += x
	}
	mutating func pop() -> T {
		return items.removeLast()
	}
	var items: T[]
}

var intStack = Stack<Int>()

Generator protocol

for x in xs {

}
// becomes
var __g = someSequence.generate()
while let x = __g.next() {
	... 
}
protocol Generator {
	type alias Element // An "associated type" requirement
	mutating fun next() -> Element?
}

A generator for Stack

struct StackGenerator<T> : Generator {
	typealias Element == T
	mutating func next() -> T? {
		if items.isEmpty { return nil }
		let ret = items[0]
		items = items[1..items.count]
		return ret
	}
	var items: Sliece<T>
}

Sequence protocol

extension Stack : Sequence {
	func generate() -> StackGenerator<T> {
		return StackGenerator( items[0..itemCount] )
	}
}

The Swift Model

  • Statically compiled
  • Small runtime

Simple interoperation

  • Transparent interaction with C and Objc-C, can deploy to previous versions of iOS and OSX

Predictable behavior

  • You control the code that runs
  • Comprehensible compilation with inspectable results
  • No non-deterministic JITs or garbage collection pauses

Efficient Execution

  • Native code instantly ready to run
  • No artificial abstraction barriers
    • abstraction disappears immediately during compilation
  • Predictable model enables bare-to-the-metal programming

Compiler Architecture

  • Swift
    • Parsing
    • High-Level Optimization
  • LLVM
    • low-level optimization
    • code generation

Reducing Abstraction Penalties

  • Global analysis of your app
  • No runtime penalty for using structs
  • Even int and Float are structs

Generic Specialization

  • Swift can run generic code directly
  • Optimizer can produce specialized versions of generic code at will
    • separate compilation of generics
    • faster compiles
    • flexibility to trade code size for speed

Devirtualization

  • Resolving dynamic method calls at compile-time
    • If Swift can see where you constructed the object
    • If Swift knows that a class doesn't have any subclasses
    • If you've marked a method with the @final attribute

High-level Optimization

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment