Slide 1

Slide 1 text

BETTER RECURSION WITH SWIFT

Slide 2

Slide 2 text

AKA…

Slide 3

Slide 3 text

RAIDERS OF THE LOST ARC Another reason why Swift value types matter.

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

FAST. MODERN. SAFE

Slide 8

Slide 8 text

FUNCTIONAL-ISH

Slide 9

Slide 9 text

let str = "Favours Immutability"

Slide 10

Slide 10 text

GOOD: CONCURRENCY

Slide 11

Slide 11 text

MEH: CACHE EFFICIENCY

Slide 12

Slide 12 text

CHALLENGE FOR ITERATION

Slide 13

Slide 13 text

func factorial(n: Int) -> Int { var result = 1 var x = n while x > 0 { result = result * x x-- } return result }

Slide 14

Slide 14 text

RECURSION Immutable iteration!

Slide 15

Slide 15 text

func factorialRec(n: Int) -> Int { if n > 0 { return n * factorialRec(n - 1) } return 1 }

Slide 16

Slide 16 text

ELEGANT.

Slide 17

Slide 17 text

YET INEFFICIENT. New call frame on each iteration.

Slide 18

Slide 18 text

STACK OVERFLOW Easier than ever with recursion!

Slide 19

Slide 19 text

All Is not Lost

Slide 20

Slide 20 text

TAIL CALLS Recursive call is performed last.

Slide 21

Slide 21 text

func tco(n: Int, acc: Int) -> Int { if n > 0 { return tco(n - 1, acc * n) } return acc }

Slide 22

Slide 22 text

func tco(n: Int, acc: Int) -> Int { if n > 0 { return tco(n - 1, acc * n) } return acc } func factorialTCO(n: Int) -> Int { return tco(n, 1) }

Slide 23

Slide 23 text

func tco(n: Int, acc: Int) -> Int { if n > 0 { return tco(n - 1, acc * n) } return acc }

Slide 24

Slide 24 text

CAN BE OPTIMIZED To something like this…

Slide 25

Slide 25 text

func factorial(n: Int) -> Int { var result = 1 var x = n while x > 0 { result = result * x x-- } return result }

Slide 26

Slide 26 text

DOES SWIFT DO THIS?

Slide 27

Slide 27 text

DOES SWIFT DO THIS? The internet says: *Sometimes*

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

Or… xcrun swiftc -S

Slide 30

Slide 30 text

AMD64 ABI Args placed in rdi, rsi, rdx, .. Return value -> rax

Slide 31

Slide 31 text

func tco(n: Int, acc: Int) -> Int rdi → n rsi → acc rax → return value

Slide 32

Slide 32 text

IS TCO HAPPENING? call → Bad - Creates new stack frame jmp, jg, jle, j… → Good - Stays in current stack frame

Slide 33

Slide 33 text

__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq $0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq

Slide 34

Slide 34 text

__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq $0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq Stack frame setup.

Slide 35

Slide 35 text

__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq $0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq Stack frame setup. if n > 0

Slide 36

Slide 36 text

__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq $0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq Stack frame setup. if n > 0

Slide 37

Slide 37 text

__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq $0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq Stack frame setup. if n > 0 Return value

Slide 38

Slide 38 text

__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq $0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq Stack frame setup. if n > 0 Return value Restore caller stack frame

Slide 39

Slide 39 text

__TF4main3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp subq $64, %rsp cmpq $0, %rdi movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) jle LBB19_4 . . movq -24(%rbp), %rdi movq -40(%rbp), %rsi callq __TF4main3tcoFTSiSi_Si movq %rax, -56(%rbp) jmp LBB19_5 LBB19_4: movq -16(%rbp), %rax movq %rax, -56(%rbp) jmp LBB19_5 LBB19_5: movq -56(%rbp), %rax addq $64, %rsp popq %rbp retq No TCO

Slide 40

Slide 40 text

OPTIMIZE

Slide 41

Slide 41 text

WALK: -Onone

Slide 42

Slide 42 text

KILL: -O

Slide 43

Slide 43 text

-Ounchecked

Slide 44

Slide 44 text

__TTSf4s_s___TF4tcoO3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp testq %rdi, %rdi jle LBB33_4 LBB33_1: movq %rdi, %rax decq %rax jo LBB33_5 imulq %rdi, %rsi jo LBB33_5 testq %rax, %rax movq %rax, %rdi jg LBB33_1 LBB33_4: movq %rsi, %rax popq %rbp retq

Slide 45

Slide 45 text

__TTSf4s_s___TF4tcoO3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp testq %rdi, %rdi jle LBB33_4 LBB33_1: movq %rdi, %rax decq %rax jo LBB33_5 imulq %rdi, %rsi jo LBB33_5 testq %rax, %rax movq %rax, %rdi jg LBB33_1 LBB33_4: movq %rsi, %rax popq %rbp retq

Slide 46

Slide 46 text

__TTSf4s_s___TF4tcoO3tcoFTSiSi_Si: pushq %rbp movq %rsp, %rbp testq %rdi, %rdi jle LBB33_4 LBB33_1: movq %rdi, %rax decq %rax jo LBB33_5 imulq %rdi, %rsi jo LBB33_5 testq %rax, %rax movq %rax, %rdi jg LBB33_1 LBB33_4: movq %rsi, %rax popq %rbp retq

Slide 47

Slide 47 text

SOMETIMES Swift Does This.

Slide 48

Slide 48 text

AUTOMATIC REFERENCE COUNTING Imagine compile time garbage collection.

Slide 49

Slide 49 text

OLDEN TIMES Memory managed by hand.

Slide 50

Slide 50 text

@interface Something() { NSNumber *_numberIvar; } @end @implementation Something - (void)setNumber:(NSNumber *)number { NSNumber *tmp = _numberIvar; _numberIvar = [number retain]; [tmp release]; } @end

Slide 51

Slide 51 text

@interface Something() { NSNumber *_numberIvar; } @end @implementation Something - (void)setNumber:(NSNumber *)number { NSNumber *tmp = _numberIvar; _numberIvar = [number retain]; [tmp release]; } @end

Slide 52

Slide 52 text

WITH ARC

Slide 53

Slide 53 text

- (void)setNumber:(NSNumber *)number { _numberIvar = number; }

Slide 54

Slide 54 text

COMPILER MAGIC Inserts retain/release calls.

Slide 55

Slide 55 text

ARC in Swift Manages references.

Slide 56

Slide 56 text

TCO + ARC

Slide 57

Slide 57 text

class Integer: IntegerLiteralConvertible { let val: Int required init(integerLiteral value: Int) { self.val = value } init(_ val: Int) { self.val = val } }

Slide 58

Slide 58 text

func +(lhs: Integer, rhs: Integer) -> Integer { return Integer(lhs.val + rhs.val) } func -(lhs: Integer, rhs: Integer) -> Integer { return Integer(lhs.val - rhs.val) } func *(lhs: Integer, rhs: Integer) -> Integer { return Integer(lhs.val * rhs.val) } func >(lhs: Integer, rhs: Integer) -> Bool { return lhs.val > rhs.val }

Slide 59

Slide 59 text

func factorialTC(n: Integer) -> Integer { return tc(n, 1) } func tc(n: Integer, acc: Integer) -> Integer { if n > 0 { return tc(n - 1, acc * n) } return acc }

Slide 60

Slide 60 text

__TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_: pushq %rbp movq %rsp, %rbp . . leaq __TMdC4tcoO7Integer+16(%rip), %rdi callq _swift_getInitializedObjCClass movq %rax, %rbx . . movq %rbx, %rdi callq _swift_allocObject movq %rax, %r15 . . movq %r15, %rdi movq %rbx, %rsi callq __TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_ movq %rax, %r14 movq %r15, %rdi callq _swift_release movq %rbx, %rdi callq _swift_release jmp LBB34_7 LBB34_7: movq %r14, %rax . . popq %rbp retq LBB34_8:

Slide 61

Slide 61 text

__TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_: pushq %rbp movq %rsp, %rbp . . leaq __TMdC4tcoO7Integer+16(%rip), %rdi callq _swift_getInitializedObjCClass movq %rax, %rbx . . movq %rbx, %rdi callq _swift_allocObject movq %rax, %r15 . . movq %r15, %rdi movq %rbx, %rsi callq __TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_ movq %rax, %r14 movq %r15, %rdi callq _swift_release movq %rbx, %rdi callq _swift_release jmp LBB34_7 LBB34_7: movq %r14, %rax . . popq %rbp retq LBB34_8:

Slide 62

Slide 62 text

__TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_: pushq %rbp movq %rsp, %rbp . . leaq __TMdC4tcoO7Integer+16(%rip), %rdi callq _swift_getInitializedObjCClass movq %rax, %rbx . . movq %rbx, %rdi callq _swift_allocObject movq %rax, %r15 . . movq %r15, %rdi movq %rbx, %rsi callq __TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_ movq %rax, %r14 movq %r15, %rdi callq _swift_release movq %rbx, %rdi callq _swift_release jmp LBB34_7 LBB34_7: movq %r14, %rax . . popq %rbp retq LBB34_8: No TCO

Slide 63

Slide 63 text

__TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_: pushq %rbp movq %rsp, %rbp . . leaq __TMdC4tcoO7Integer+16(%rip), %rdi callq _swift_getInitializedObjCClass movq %rax, %rbx . . movq %rbx, %rdi callq _swift_allocObject movq %rax, %r15 . . movq %r15, %rdi movq %rbx, %rsi callq __TTSf4g_g___TF4tcoO2tcFTCS_7IntegerS0__S0_ movq %rax, %r14 movq %r15, %rdi callq _swift_release movq %rbx, %rdi callq _swift_release jmp LBB34_7 LBB34_7: movq %r14, %rax . . popq %rbp retq LBB34_8: No TCO Last Action Performed

Slide 64

Slide 64 text

Integer created on +,-,* ops

Slide 65

Slide 65 text

func +(lhs: Integer, rhs: Integer) -> Integer { return Integer(lhs.val + rhs.val) } func -(lhs: Integer, rhs: Integer) -> Integer { return Integer(lhs.val - rhs.val) } func *(lhs: Integer, rhs: Integer) -> Integer { return Integer(lhs.val * rhs.val) }

Slide 66

Slide 66 text

NEEDS RELEASING

Slide 67

Slide 67 text

func tc(n: Integer, acc: Integer) -> Integer { if n > 0 { return tc(n - 1, acc * n) } return acc } SO THIS…

Slide 68

Slide 68 text

func tc(n: Integer, acc: Integer) -> Integer { let _n1 = n - 1 let _accn = acc * n if n > 0 { let tmp = tc(_n1, _accn) swift_release(_accn) swift_release(_n1) return tmp } return acc } IS REALLY SOMETHING LIKE

Slide 69

Slide 69 text

All Is sometimes Lost

Slide 70

Slide 70 text

FUNCTIONAL SWIFT Use value types.

Slide 71

Slide 71 text

VALUE SEMANTICS ≠ VALUE TYPES

Slide 72

Slide 72 text

SO.. BE CAREFULL

Slide 73

Slide 73 text

THANKS!

Slide 74

Slide 74 text

github.com/haveahennessy @haveahennessy

Slide 75

Slide 75 text

http://www.hopperapp.com/