Slide 1

Slide 1 text

Pattern Matching and Generics Austin Zheng

Slide 2

Slide 2 text

Enums

Slide 3

Slide 3 text

Basic Enum enum Direction { case North case South case East case West } ! let myDirection = Direction.North

Slide 4

Slide 4 text

Raw Value Enum enum Title : String { case CEO = "Chief Executive Officer" case CTO = "Chief Technical Officer" case CFO = "Chief Financial Officer" } ! let myTitle = Title.CEO let myString : String = Title.CEO.toRaw() let anotherTitle : Title = Title.fromRaw("Chief Executive Officer")!

Slide 5

Slide 5 text

Raw Value Enum (2) enum Planet : Int { case Mercury = 1 case Venus, Earth, Mars // 2, 3, 4 case Jupiter = 100 case Saturn, Uranus, Neptune // 101, 102, 103 } Int-valued enums count up implicitly

Slide 6

Slide 6 text

Associated Value Enum enum Barcode { case UPCA(sys: Int, data: Int, check: Int) case QRCode(String) } ! let myUPC = Barcode.UPCA(sys: 0, data: 27917_01919, check: 2) let myQRCode = Barcode.QRCode("http://example.com")

Slide 7

Slide 7 text

Associated Value Enum (2) enum JSONNode { case NullNode case StringNode(String) case NumberNode(Float) case BoolNode(Bool) case ArrayNode([JSONNode]) case ObjectNode([String:JSONNode]) } ! let x : JSONNode = .ArrayNode( [.NumberNode(10.0), .StringNode("hello"), .BoolNode(false)])

Slide 8

Slide 8 text

Switch Statement (Pattern Matching)

Slide 9

Slide 9 text

Basic Switch Statement let value = 10 switch value { case 10: println("ten") case 20: println("twenty") case 30: println("thirty") default: println("another number") }

Slide 10

Slide 10 text

Strings let e: String = "France" switch e { case "France": println("!") case "Germany": println(""") case "Italy": println("#") case "Russia": println("$") case "Spain": println("%") case "United Kingdom": println("&") default: println("Other") } (actually, any Equatable type)

Slide 11

Slide 11 text

Ranges let v: UInt = 10 switch v { case 0...9: println("Single digit") case 10...99: println("Double digits") case 100...999: println("Triple digits") default: println("4 or more digits") }

Slide 12

Slide 12 text

Tuples let person = ("Helen", 25) switch person { case ("Helen", let age): println("Your name is Helen, and you are \(age)" + " years old") case (_, 13...19): println("You are a teenager") case ("Bob", _): println("You are not a teenager, but your name" + " is Bob.") case (_, _): println("no comment") }

Slide 13

Slide 13 text

Binding in cases let myTuple = ("abcd", 1234) switch myTuple { case let (x, y): "The string in 'x' is \(x); " + "the integer in 'y' is \(y)" case (let x, let y): "Another way to do the exact same thing" case (_, let y): "We don't care about the string in 'x', " + "but the integer in 'y' is \(y)" }

Slide 14

Slide 14 text

Enums enum ParseResult { case NumericValue(Int) case Error(String) } ! let a = ParseResult.NumericValue(1) switch a { case let .NumericValue(v): "Success; numeric value is \(v)" case .Error(let err): "Failed; error message is \(err)" }

Slide 15

Slide 15 text

Switching on Types let myView : UIView = getView() switch myView { case is UIImageView: println("It's an image view") case let lbl as UILabel: println("It's a label, with text \(lbl.text)") case let tv as UITableView: println("It's a table view, with" + " \(tv.numberOfSections()) sections") default: println("It's some other type of view") }

Slide 16

Slide 16 text

where clause let myView : UIView = getView() switch myView { case _ where myView.frame.size.height < 50: println("Your view is shorter than 50 units") case _ where myView.frame.size.width > 20: println("Your view is at least 50 units tall," + " and is more than 20 units wide") case _ where myView.backgroundColor == UIColor.greenColor(): println("Your view is at least 50 units tall," + " at most 20 units wide, and is green.") default: println("I can't describe your view.") }

Slide 17

Slide 17 text

Expression Operator func ~=(pattern: Type1, value: Type2) -> Bool

Slide 18

Slide 18 text

Expression Operator switch someFoo { case pattern1: doSomething() case pattern2: doSomethingElse() // ... default: doDefault() } let r = someFoo if pattern1 ~= r { doSomething() } else if pattern2 ~= r { doSomethingElse() } // ... else { doDefault() } (sort of)

Slide 19

Slide 19 text

What if…? let myArray = [1, 2, 4, 3] switch myArray { case [..., 0, 0, 0]: doSomething() case [4, ...]: doSomething() case [_, 2, _, 4]: doSomething() case [_, _, 3, _]: doSomething() case [_, _, _, 3]: doSomething() default: doDefault() }

Slide 20

Slide 20 text

Expression Pattern enum WCEnum { case Wildcard case FromBeginning case ToEnd case Literal(Int) }

Slide 21

Slide 21 text

Expression Pattern func ~=(pattern: [WCEnum], value: [Int]) -> Bool { var ctr = 0 for currentPattern in pattern { if ctr >= value.count || ctr < 0 { return false } let currentValue = value[ctr] switch currentPattern { case .Wildcard: ctr++ case .FromBeginning where ctr == 0: ctr = (value.count - pattern.count + 1) case .FromBeginning: return false case .ToEnd: return true case .Literal(let v): if v != currentValue { return false } else { ctr++ } } } return true }

Slide 22

Slide 22 text

Expression Pattern (2) let myArray = [1, 2, 4, 3] switch myArray { case [.FromBeginning, .Literal(0), .Literal(0), .Literal(0)]: "last three elements are 0" case [.Literal(4), .ToEnd]: "first element is 4, everything else doesn't matter" case [.Wildcard, .Literal(2), .Wildcard, .Literal(4)]: "4 elements; second element is 2, fourth element is 4" case [.Wildcard, .Wildcard, .Literal(3), .Wildcard]: "third element (out of 4) is 3" case [.Wildcard, .Wildcard, .Wildcard, .Literal(3)]: "fourth element (out of 4) is 3" default: "catchall" }

Slide 23

Slide 23 text

Expression Patterns (3) let anotherArray = [2, 10 ,0, 0, 0] switch anotherArray { case [<~nil, *0, *0, *0]: "last three elements are 0" case [*4, nil~>]: "first element is 4" case [nil, *2, nil, *2]: "4 elements; 2nd is 2, 4th is 4" case [nil, nil, *3, nil]: "third element (out of 4) is 3" case [nil, nil, nil, *3]: "fourth element (out of 4) is 3" default: "catchall" }

Slide 24

Slide 24 text

Protocols

Slide 25

Slide 25 text

Protocols? protocol MyProtocol { var someProperty : Int { get set } func barFunc (x: Int, y: Int) -> String } Property (get or get/set) Method

Slide 26

Slide 26 text

Conformance? class MyClass { // Nothing here... }

Slide 27

Slide 27 text

class MyClass : MyProtocol { // Nothing here... }

Slide 28

Slide 28 text

Conformance class MyClass : MyProtocol { var myProperty : Int = 0 // Property conformance func barFunc (x: Int, y: Int) -> String { return "\(x) + \(y) = \(x + y)" } var someProperty : Int { get { return myProperty } set { myProperty = newValue } } }

Slide 29

Slide 29 text

Uses? • Parent-child relationships 
 (e.g. UITableViewDelegate) • Aggregating functionality by composition
 (e.g. Equatable, LogicValue, AbsoluteValuable) • Allow disparate types to be described by the same generic type parameter (careful)

Slide 30

Slide 30 text

Playing With Fire… protocol JSONType { } extension String : JSONType { } extension Float : JSONType { } extension Bool : JSONType { } extension Array : JSONType { } extension Dictionary : JSONType { } ! let b : Array = [10.1, 10.2, "foo"] let a : Array = [10.0, "bar", false, b] Note we can’t extend e.g. Array

Slide 31

Slide 31 text

A Few More Notes… • Types can conform to zero, one, or multiple protocols • Protocols can ‘inherit’ from other protocols - they implicitly get their parent’s methods/properties • Protocols are not traits - can’t specify implementation

Slide 32

Slide 32 text

Generics

Slide 33

Slide 33 text

Simple Example func swapInts(inout this: Int, inout that: Int) { let tempThis = this this = that that = tempThis }

Slide 34

Slide 34 text

func swapStrings(inout this: String, inout that: String) { let tempThis = this this = that that = tempThis }

Slide 35

Slide 35 text

func swapItems(inout this: T, inout that: T) { let tempThis = this this = that that = tempThis } Type Parameter

Slide 36

Slide 36 text

Another Example func firstAndLastAreEqual (someArray: Array) -> Bool { let first = someArray[0] let last = someArray[someArray.count - 1] return first == last } How do we know the type ’T’ is equatable?

Slide 37

Slide 37 text

func firstAndLastAreEqual (someArray: Array) -> Bool { let first = someArray[0] let last = someArray[someArray.count - 1] return first == last } T is any type… …that conforms to Equatable

Slide 38

Slide 38 text

Generics • Functions or types (e.g. classes) can be generic • Type information is available at runtime • Compiler can optimize by creating specific versions for common cases (C++ templates), and fall back to the generic implementation

Slide 39

Slide 39 text

protocol InitProtocol { init() } ! struct SomeStruct { var x: T? = nil func instantiate() -> T { return T() } } ! struct AnotherStruct: InitProtocol { func favoriteString() -> String { return "reify me please" } } ! let c = SomeStruct() let d = c.instantiate() d.favoriteString() ‘?’ just means this is allowed to be nil

Slide 40

Slide 40 text

Extended Example

Slide 41

Slide 41 text

Our Goal • We want a function that can take a collection, take an array, and append the items in that collection to the array • We want this function to be typesafe • We want to write one function that handles every case • Array, Dictionary, SquareMatrix, TreeNode

Slide 42

Slide 42 text

My Square Matrix struct SquareMatrix { let d: Int; var backingArray: [T] init(dimension d: Int, initial: T) { self.d = d backingArray = [T](count: d*d, repeatedValue: initial) } subscript(row: Int, col: Int) -> T { get { return backingArray[row*d + col] } set { backingArray[row*d + col] = newValue } } }

Slide 43

Slide 43 text

My Tree class TreeNode { let value : T let leftNode : TreeNode? let rightNode : TreeNode? init(value: T, left: TreeNode?, right: TreeNode?) { self.value = value self.leftNode = left self.rightNode = right } } An aside: would have preferred to use enums.

Slide 44

Slide 44 text

Let’s define a protocol… protocol AllElements { typealias ElementType // Return an array containing all the objects // in the collection func allElements() -> Array }

Slide 45

Slide 45 text

Associated Types protocol AllElements { typealias ElementType func allElements() -> Array } Determined by what the conforming type returns for its implementation of allElements()

Slide 46

Slide 46 text

Associated Types protocol FooProtocol { typealias SomeType func fooFunc() -> SomeType } ! struct Bar : FooProtocol { func fooFunc() -> String { return "bleh" } } Bar.SomeType == String

Slide 47

Slide 47 text

Also… protocol NilLiteralConvertible { class func convertFromNilLiteral() -> Self } Is equal to the conforming type

Slide 48

Slide 48 text

Extend Array extension Array : AllElements { func allElements() -> Array { return self } } Extension: add methods, computed properties, protocol conformance to existing type

Slide 49

Slide 49 text

Extend Dictionary extension Dictionary : AllElements { func allElements() -> Array { var buffer = Array() for (_, value) in self { buffer.append(value) } return buffer } }

Slide 50

Slide 50 text

Extend SquareMatrix extension SquareMatrix : AllElements { func allElements() -> Array { return backingArray } }

Slide 51

Slide 51 text

Extend Tree extension TreeNode : AllElements { func allElements() -> Array { var buffer = Array() if leftNode { buffer += leftNode!.allElements() } buffer.append(self.value) if rightNode { buffer += rightNode!.allElements() } return buffer } }

Slide 52

Slide 52 text

Put it all together… func appendToArray (source: T, inout dest: Array) { ! let a = source.allElements() for element in a { dest.append(element) } } ! var buffer = ["a", "b", "c"] appendToArray([1: "foo", 2: "bar"], &buffer)

Slide 53

Slide 53 text

“But…we ended up writing five methods!”

Slide 54

Slide 54 text

An Aside • In real life: use Sequence, for-in loop • See: Generator, EnumerateGenerator, etc • http://www.scottlogic.com/blog/2014/06/26/swift-sequences.html • Still have to implement Sequence for custom types…but you get all Sequence functionality

Slide 55

Slide 55 text

Generic Argument Clause

Slide 56

Slide 56 text

Before ‘where’ • Declare type arguments (e.g. T, U) • In this declaration, a type argument can conform to zero or one protocols • If you want T to conform to 2+ protocols…add a ‘where’ clause to constrain

Slide 57

Slide 57 text

What Can You Constrain? • Any free type arguments you declared earlier
 (e.g. T, U) • Any associated types associated with the type arguments via protocols (e.g. T.SomeType)

Slide 58

Slide 58 text

How Can You Constrain? • Specify that a type implements a protocol 
 (e.g. T.SomeType : MyProtocol) • Specify that a class inherits from another class
 (e.g. T : SomeParentClass) • Specify equality of types
 (e.g. T.ContainerType == U.ContainerType)

Slide 59

Slide 59 text

Revisiting ~= enum WCEnum { case Wildcard case FromBeginning case ToEnd case Literal(T) } ! func ~=(pattern: [WCEnum], value: [T]) -> Bool { // ... }

Slide 60

Slide 60 text

No content