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
Matt Isaacs
August 26, 2015
Programming
1
600
Better Recursion With Swift
Presented at the Brooklyn Swift Developers Meetup, August 2015.
Matt Isaacs
August 26, 2015
Tweet
Share
More Decks by Matt Isaacs
See All by Matt Isaacs
iOS Testing with Appium at Gilt
ontherocks
0
140
Mobile Testing at Gilt
ontherocks
0
48
Other Decks in Programming
See All in Programming
AI Agent の開発と運用を支える Durable Execution #AgentsInProd
izumin5210
7
2.3k
360° Signals in Angular: Signal Forms with SignalStore & Resources @ngLondon 01/2026
manfredsteyer
PRO
0
120
Lambda のコードストレージ容量に気をつけましょう
tattwan718
0
110
humanlayerのブログから学ぶ、良いCLAUDE.mdの書き方
tsukamoto1783
0
190
ぼくの開発環境2026
yuzneri
0
190
AI & Enginnering
codelynx
0
110
CSC307 Lecture 08
javiergs
PRO
0
670
AI によるインシデント初動調査の自動化を行う AI インシデントコマンダーを作った話
azukiazusa1
1
710
0→1 フロントエンド開発 Tips🚀 #レバテックMeetup
bengo4com
0
560
それ、本当に安全? ファイルアップロードで見落としがちなセキュリティリスクと対策
penpeen
7
3.8k
HTTPプロトコル正しく理解していますか? 〜かわいい猫と共に学ぼう。ฅ^•ω•^ฅ ニャ〜
hekuchan
2
680
Architectural Extensions
denyspoltorak
0
280
Featured
See All Featured
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.3k
Information Architects: The Missing Link in Design Systems
soysaucechin
0
770
Stop Working from a Prison Cell
hatefulcrawdad
273
21k
Designing for Performance
lara
610
70k
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
63
Site-Speed That Sticks
csswizardry
13
1.1k
The Language of Interfaces
destraynor
162
26k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.1k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
0
140
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
0
2.3k
What does AI have to do with Human Rights?
axbom
PRO
0
2k
Mobile First: as difficult as doing things right
swwweet
225
10k
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/