Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Better Recursion With Swift
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Matt Isaacs
August 26, 2015
Programming
630
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Better Recursion With Swift
Presented at the Brooklyn Swift Developers Meetup, August 2015.
Matt Isaacs
August 26, 2015
More Decks by Matt Isaacs
See All by Matt Isaacs
iOS Testing with Appium at Gilt
ontherocks
0
150
Mobile Testing at Gilt
ontherocks
0
50
Other Decks in Programming
See All in Programming
DroidKaigi 2026 「個人開発という実験場: Android エンジニアが手にする4つの自由」
slashnephy
0
220
iOS開発×AI駆動開発 〜最近使って便利だったスキルの話〜
nogu66
0
140
高専キャリア LT 発表内容
crysta1221
6
5.6k
一参加者から『中の人』へ 〜全通PHPerがブースに立って学んだ、カンファレンスを100倍楽しむコツ〜
wp_daisuke
0
120
Hono + Inertia + React で LP を構築した話
oukayuka
2
220
kubernetes コンポーネント開発入門 / 新卒N年目の勉強会&交流会!〜〇〇への誘い〜 #n_study
mazrean
0
170
PyO3 で既存 Python 評価器を Rust core 化する ー wasm-bindgen でブラウザにも配るための設計
kdash
1
520
LoopHub - ローカルで動く GitHub で、AI と共同開発
jugyo
0
450
異なる設計思想のフレームワークを経験して得た学び
amekuhideki
2
1.5k
Vibes Containers 〜AIで変わるコンテナ設計と運用〜
tkikuc
2
500
業務時間外もAIに働いてもらう話
colorful12
3
10k
AIの中の人になってみる
htkym
0
170
Featured
See All Featured
Into the Great Unknown - MozCon
thekraken
41
2.7k
Mind Mapping
helmedeiros
1
350
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
270
Game over? The fight for quality and originality in the time of robots
wayneb77
1
270
We Have a Design System, Now What?
morganepeng
55
8.3k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2.1k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.4k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.6k
GraphQLとの向き合い方2022年版
quramy
50
15k
Tell your own story through comics
letsgokoyo
1
1.1k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.8k
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
430
Transcript
BETTER RECURSION WITH SWIFT
AKA…
RAIDERS OF THE LOST ARC Another reason why Swift value
types matter.
None
None
None
FAST. MODERN. SAFE
FUNCTIONAL-ISH
let str = "Favours Immutability"
GOOD: CONCURRENCY
MEH: CACHE EFFICIENCY
CHALLENGE FOR ITERATION
func factorial(n: Int) -> Int { var result = 1
var x = n while x > 0 { result = result * x x-- } return result }
RECURSION Immutable iteration!
func factorialRec(n: Int) -> Int { if n > 0
{ return n * factorialRec(n - 1) } return 1 }
ELEGANT.
YET INEFFICIENT. New call frame on each iteration.
STACK OVERFLOW Easier than ever with recursion!
All Is not Lost
TAIL CALLS Recursive call is performed last.
func tco(n: Int, acc: Int) -> Int { if n
> 0 { return tco(n - 1, acc * n) } return acc }
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) }
func tco(n: Int, acc: Int) -> Int { if n
> 0 { return tco(n - 1, acc * n) } return acc }
CAN BE OPTIMIZED To something like this…
func factorial(n: Int) -> Int { var result = 1
var x = n while x > 0 { result = result * x x-- } return result }
DOES SWIFT DO THIS?
DOES SWIFT DO THIS? The internet says: *Sometimes*
None
Or… xcrun swiftc -S
AMD64 ABI Args placed in rdi, rsi, rdx, .. Return
value -> rax
func tco(n: Int, acc: Int) -> Int rdi → n
rsi → acc rax → return value
IS TCO HAPPENING? call → Bad - Creates new stack
frame jmp, jg, jle, j… → Good - Stays in current stack frame
__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
__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.
__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
__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
__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
__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
__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
OPTIMIZE
WALK: -Onone
KILL: -O
-Ounchecked
__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
__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
__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
SOMETIMES Swift Does This.
AUTOMATIC REFERENCE COUNTING Imagine compile time garbage collection.
OLDEN TIMES Memory managed by hand.
@interface Something() { NSNumber *_numberIvar; } @end @implementation Something -
(void)setNumber:(NSNumber *)number { NSNumber *tmp = _numberIvar; _numberIvar = [number retain]; [tmp release]; } @end
@interface Something() { NSNumber *_numberIvar; } @end @implementation Something -
(void)setNumber:(NSNumber *)number { NSNumber *tmp = _numberIvar; _numberIvar = [number retain]; [tmp release]; } @end
WITH ARC
- (void)setNumber:(NSNumber *)number { _numberIvar = number; }
COMPILER MAGIC Inserts retain/release calls.
ARC in Swift Manages references.
TCO + ARC
class Integer: IntegerLiteralConvertible { let val: Int required init(integerLiteral value:
Int) { self.val = value } init(_ val: Int) { self.val = 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) -> Integer { return Integer(lhs.val * rhs.val) } func >(lhs: Integer, rhs: Integer) -> Bool { return lhs.val > rhs.val }
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 }
__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:
__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:
__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
__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
Integer created on +,-,* ops
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) }
NEEDS RELEASING
func tc(n: Integer, acc: Integer) -> Integer { if n
> 0 { return tc(n - 1, acc * n) } return acc } SO THIS…
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
All Is sometimes Lost
FUNCTIONAL SWIFT Use value types.
VALUE SEMANTICS ≠ VALUE TYPES
SO.. BE CAREFULL
THANKS!
github.com/haveahennessy @haveahennessy
http://www.hopperapp.com/