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
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
49
Other Decks in Programming
See All in Programming
Google Apps Script で Ruby を動かす
kawahara
0
280
ここ半年くらいでAIに作らせたR用ツール
eitsupi
0
390
複数の Claude Code が"放置"されてしまう問題をCLI ダッシュボードを自作して解決した話
sumihiro3
1
710
AI Readyの正体はデータマネジメントだ メダリオン2.0の最前線
freee
PRO
0
320
源内ハンズオン概要編
hideg
0
190
人間の目はかわらない、だからJPEGは30年もつ
yuzneri
12
19k
KotlinConf Extended South Korea 2026 Keynote
l2hyunwoo
0
110
【QA Test Talk Vol.8】AI-DLC による Whole Team Approach の加速
pkshadeck
PRO
0
210
まずはプロンプトガイドを読もう、話はそれからだ
kiakiraki
0
140
型も通る、synthも通る、それでも危ない 〜AIのCDKの権限とコストを機械で検証する〜 / It Passes Type Checks, It Passes Synth Checks, but It’s Still Risky — Automatically Verifying Permissions and Costs in AI’s CDK —
seike460
PRO
1
570
生成AIで帳票OCRが「簡単に」作れる時代になった?
kon_shou
0
1.1k
What's New in Android 2026
veronikapj
0
270
Featured
See All Featured
We Have a Design System, Now What?
morganepeng
55
8.3k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.8k
30 Presentation Tips
portentint
PRO
1
370
Building Applications with DynamoDB
mza
96
7.2k
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
650
Designing for Timeless Needs
cassininazir
1
450
Visualization
eitanlees
152
17k
Code Review Best Practice
trishagee
74
20k
Context Engineering - Making Every Token Count
addyosmani
9
1.1k
WENDY [Excerpt]
tessaabrams
11
39k
Speed Design
sergeychernyshev
33
2k
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/