-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathswift-sequence-inside-the-compiler-how-for-loops-work.html
356 lines (310 loc) · 21.1 KB
/
swift-sequence-inside-the-compiler-how-for-loops-work.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://use.fontawesome.com/afd448ce82.js"></script>
<!-- Meta Tag -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- SEO -->
<meta name="author" content="Bruno Rocha">
<meta name="keywords" content="Software, Engineering, Blog, Posts, iOS, Xcode, Swift, Articles, Tutorials, OBJ-C, Objective-C, Apple">
<meta name="description" content="In this article, I'll cover one of my favorite syntax sugars in the language: the protocols and the internal compiler behavior that allow you to write for loops. You might have heard already of the Sequence family of protocols, and here, we'll see how the compiler uses them as the building blocks for for loops.">
<meta name="title" content="Swift's Sequence Inside The Compiler: How for loops work internally">
<meta name="url" content="https://swiftrocks.com/swift-sequence-inside-the-compiler-how-for-loops-work">
<meta name="image" content="https://swiftrocks.com/images/thumbs/thumb.jpg?4">
<meta name="copyright" content="Bruno Rocha">
<meta name="robots" content="index,follow">
<meta property="og:title" content="Swift's Sequence Inside The Compiler: How for loops work internally"/>
<meta property="og:image" content="https://swiftrocks.com/images/thumbs/thumb.jpg?4"/>
<meta property="og:description" content="In this article, I'll cover one of my favorite syntax sugars in the language: the protocols and the internal compiler behavior that allow you to write for loops. You might have heard already of the Sequence family of protocols, and here, we'll see how the compiler uses them as the building blocks for for loops."/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://swiftrocks.com/swift-sequence-inside-the-compiler-how-for-loops-work"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://swiftrocks.com/images/thumbs/thumb.jpg?4"/>
<meta name="twitter:image:alt" content="Page Thumbnail"/>
<meta name="twitter:title" content="Swift's Sequence Inside The Compiler: How for loops work internally"/>
<meta name="twitter:description" content="In this article, I'll cover one of my favorite syntax sugars in the language: the protocols and the internal compiler behavior that allow you to write for loops. You might have heard already of the Sequence family of protocols, and here, we'll see how the compiler uses them as the building blocks for for loops."/>
<meta name="twitter:site" content="@rockbruno_"/>
<!-- Favicon -->
<link rel="icon" type="image/png" href="images/favicon/iconsmall2.png" sizes="32x32" />
<link rel="apple-touch-icon" href="images/favicon/iconsmall2.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap" rel="stylesheet">
<!-- Bootstrap CSS Plugins -->
<link rel="stylesheet" type="text/css" href="css/bootstrap.css">
<!-- Prism CSS Stylesheet -->
<link rel="stylesheet" type="text/css" href="css/prism4.css">
<!-- Main CSS Stylesheet -->
<link rel="stylesheet" type="text/css" href="css/style48.css">
<link rel="stylesheet" type="text/css" href="css/sponsor4.css">
<!-- HTML5 shiv and Respond.js support IE8 or Older for HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://swiftrocks.com/swift-sequence-inside-the-compiler-how-for-loops-work"
},
"image": [
"https://swiftrocks.com/images/thumbs/thumb.jpg"
],
"datePublished": "2019-10-25T17:50:00+00:00",
"dateModified": "2020-04-12T14:00:00+02:00",
"author": {
"@type": "Person",
"name": "Bruno Rocha"
},
"publisher": {
"@type": "Organization",
"name": "SwiftRocks",
"logo": {
"@type": "ImageObject",
"url": "https://swiftrocks.com/images/thumbs/thumb.jpg"
}
},
"headline": "Swift's Sequence Inside The Compiler: How for loops work internally",
"abstract": "In this article, I'll cover one of my favorite syntax sugars in the language: the protocols and the internal compiler behavior that allow you to write for loops. You might have heard already of the Sequence family of protocols, and here, we'll see how the compiler uses them as the building blocks for for loops."
}
</script>
</head>
<body>
<div id="main">
<!-- Blog Header -->
<!-- Blog Post (Right Sidebar) Start -->
<div class="container">
<div class="col-xs-12">
<div class="page-body">
<div class="row">
<div><a href="https://swiftrocks.com">
<img id="logo" class="logo" alt="SwiftRocks" src="images/bg/logo2light.png">
</a>
<div class="menu-large">
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-large">
<div class="menu-item">
<a href="blog">blog</a>
</div>
<div class="menu-item">
<a href="about">about</a>
</div>
<div class="menu-item">
<a href="talks">talks</a>
</div>
<div class="menu-item">
<a href="projects">projects</a>
</div>
<div class="menu-item">
<a href="software-engineering-book-recommendations">book recs</a>
</div>
<div class="menu-item">
<a href="games">game recs</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
</div>
<div class="menu-small">
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-small-1">
<div class="menu-item">
<a href="blog">blog</a>
</div>
<div class="menu-item">
<a href="about">about</a>
</div>
<div class="menu-item">
<a href="talks">talks</a>
</div>
<div class="menu-item">
<a href="projects">projects</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-small-2">
<div class="menu-item">
<a href="software-engineering-book-recommendations">book recs</a>
</div>
<div class="menu-item">
<a href="games">game recs</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
</div>
</div>
<div class="content-page" id="WRITEIT_DYNAMIC_CONTENT">
<!--WRITEIT_POST_NAME=Swift's Sequence Inside The Compiler: How for loops work internally-->
<!--WRITEIT_POST_HTML_NAME=swift-sequence-inside-the-compiler-how-for-loops-work-->
<!--WRITEIT_POST_SITEMAP_DATE_LAST_MOD=2020-04-12T14:00:00+02:00-->
<!--WRITEIT_POST_SITEMAP_DATE=2019-10-25T17:50:00+00:00-->
<!--Add here the additional properties that you want each page to possess.-->
<!--These properties can be used to change content in the template page or in the page itself as shown here.-->
<!--Properties must start with 'WRITEIT_POST'.-->
<!--Writeit provides and injects WRITEIT_POST_NAME and WRITEIT_POST_HTML_NAME by default.-->
<!--WRITEIT_POST_SHORT_DESCRIPTION=In this article, I'll cover one of my favorite syntax sugars in the language: the protocols and the internal compiler behavior that allow you to write for loops. You might have heard already of the Sequence family of protocols, and here, we'll see how the compiler uses them as the building blocks for for loops.-->
<title>Swift's Sequence Inside The Compiler: How for loops work internally</title>
<div class="blog-post">
<div class="post-title-index">
<h1>Swift's Sequence Inside The Compiler: How <code>for</code> loops work internally</h1>
</div>
<div class="post-info">
<div class="post-info-text">
Published on 26 Oct 2019
</div>
</div>
<p>One of my favorite things about Swift is that almost every compiler feature is built on top of actual classes and protocols of the language. This means that if you see a native type that has some special magical property (like SwiftUI's declarative syntax, which was covered in another post here in SwiftRocks), it's likely that you can reproduce that behavior in your custom types.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>In this article, I'll cover one of my favorite syntax sugars in the language: the protocols and the internal compiler behavior that allow you to write <code>for</code> loops. You might have heard already of the <code>Sequence</code> family of protocols, and here, we'll see how the compiler uses them as the building blocks for <code>for</code> loops.</p>
<pre><code>for i in 0..<10 {
print("Wait, this is not a hardcoded language thing?")
}</code></pre>
<p>In most languages, the ability to iterate simple data types like integers in <code>for</code> statements is often hardcoded into the compiler. This was no different in the first few versions of Swift where the standard was to use old C-style loops:</p>
<pre><code>// Swift 1.2
for var i = 0; i < 10; i++ {
print(i)
}</code></pre>
<p><code>for in</code> style loops came along with the <code>Range</code> type, and the release of Swift 3 officially removed the old C-style loops from the language and forced everyone to use the <code>for in</code> loops that we are all used to. Now, <code>for</code> loops in Swift are based on <b>actual protocols</b> that you can inherit to add <code>for in</code> capabilities to <b>anything</b> you create that can be represented as such. As expected, this means that the ability to use ranges as the parameter of a loop isn't because ranges are treated differently in the compiler -- but because ranges conform to specific public protocols.</p>
<h2>IteratorProtocol</h2>
<p>The base of how iteration in general works in Swift is the <code>IteratorProtocol</code> type -- a very simple protocol that only takes two components: An <code>associatedtype</code> that represents what's being iterated, and a <code>next</code> property that returns the "next" element on the iteration (or nil if it's over).</p>
<p>A basic example of something that can be implemented as an <code>IteratorProtocol</code> is a countdown timer that keeps reducing a value until it reaches zero:</p>
<pre><code>struct CountdownIterator: IteratorProtocol {
typealias Element = Int
var count: Int
mutating func next() -> Int? {
if count == 0 {
return nil
} else {
self.count -= 1
return count
}
}
init(count: Int) {
self.count = count
}
}</code></pre>
<p>If we access this iterator continuously we'll get diminishing values until the iteration ends, represented by nil:</p>
<pre><code>var iterator = CountdownIterator(count: 3)
iterator.next() // 2
iterator.next() // 1
iterator.next() // 0
iterator.next() // nil
iterator.next() // nil</code></pre>
<p>Iterators are said to be <b>destructive</b> because you can't access a specific value more than once -- you need to go from the beginning to the end in a linear fashion and you can only do it. If you need to access a value again, you need to create another iterator and repeat the process.</p>
<h2>Sequence</h2>
<p>In terms of iteration, the <code>Sequence</code> protocol is what we interact with the most. In short, a <code>Sequence</code> is simply a more high level abstraction of a <code>IteratorProtocol</code> type that provides additional functionality on top of the regular iteration process. While Swift allows you to create <code>Sequences</code> that are also <code>IteratorProtocols</code>, the existence of this protocol allows you to separate the definition of the type itself from the logic that handles its iteration. To use it, all you have to do is create a <code>makeIterator()</code> type that returns an <code>IteratorProtocol</code>:</p>
<pre><code>struct Countdown: Sequence {
typealias Iterator = CountdownIterator
let count: Int
init(count: Int) {
self.count = count
}
func makeIterator() -> CountdownIterator {
return CountdownIterator(count: count)
}
}</code></pre>
<p>The implementation requirements of the <code>Sequence</code> protocol might make it look useless, but don't judge a book by its cover -- it actually contains tons of required methods, but they are all implemented for you in the standard library. <code>Sequence</code> contains pre-definitions for <b>lots</b> of the commonly used sequence-based algorithms, including <code>map</code>, <code>filter</code> and <code>reduce</code>!</p>
<p>Even though <code>IteratorProtocol</code> is the type that defines how the iteration happens, <code>Sequence</code> is the type that provides the additional properties and methods that make these iterations useful in the first place:</p>
<pre><code>let countdown = Countdown(count: 3)
countdown.map { $0 * $0 }
countdown.allSatisfy { $0.isMultiple(of: 2) }
countdown.max()
countdown.sorted()
countdown.forEach { print($0) }</code></pre>
<p>Just like in <code>IteratorProtocol</code>, we can create an iterator and linearly retrieve values from it. This is how all these properties and methods work -- for example, this is how we could implement a clone of <code>map()</code>:</p>
<pre><code>extension Sequence {
func map<T>(_ transform: (Iterator.Element) throws -> T) rethrows -> [T] {
var iterator = makeIterator()
var result = [T]()
while let next = iterator.next() {
result.append(try transform(next))
}
return result
}
}</code></pre>
<p>But this isn't all -- the greatest property of <code>Sequences</code> and the reason this article was conceived is that <code>Sequences</code> <b>are deeply tied to the Swift compiler in the form of for loops.</b> Like mentioned before, the reason you can use ranges in loops is not because ranges are special, but simply because ranges are <code>Sequences</code>. Because our example <code>Countdown</code> type is a <code>Sequence</code>, it can also be used inside a <code>for</code> loop!</p>
<pre><code>for duration in Countdown(count: 3) {
print(duration)
}</code></pre>
<p>As one would expect, this will work with any type that conforms to <code>Sequence</code>.</p>
<h2>How for loops work internally in Swift</h2>
<p>Interestingly, this behavior isn't new to iOS -- old-timers may remember that you could unlock for-each style loops in Objective-C in a similar fashion by making objects inherit from <code>NSFastEnumerator</code>:</p>
<pre><code>for (id object in objectThatConformsToFastEnumerator) {
NSLog(@"%@", object);
}</code></pre>
<p>But unlike back then, with Swift we can actually see what's happening under the hood. <code>for</code> exists inside the compiler for the purpose of getting your code to be parsed correctly, but the final code is actually different. To inspect this, what I like to do is to compile an example file and have Swift dump its final <b>Swift Intermediate Language</b> representation:</p>
<pre class="command-line language-bash" data-host="SwiftRocks"><code>swiftc -emit-sil forLoopTest.swift</code></pre>
<p>If I run this command with an example <code>forLoopTest.swift</code> file that contains an <code>for</code> loop for an array such as the one below, we'll see things like this around the area where the loop was supposed to be defined:</p>
<pre><code>let array = [1,2,3]
for num in array {
print(num)
}</code></pre>
<pre><code>// function_ref Collection<>.makeIterator()
%40 = function_ref ...
// function_ref IndexingIterator.next()
%40 = function_ref ...
switch_enum %43 : $Optional<Int>, case #some!enumelt.1: bb3, case #none!enumelt: bb2</code></pre>
<p>This shows us that <code>for</code> loops don't really exist -- they are simply a form of syntax sugar to allows you to effortlessly iterate an <code>IteratorProtocol</code> without having to actually extract the iterator!</p>
<p>We can get more details by seeing Swift's source code, more specifically the classes that handle the <b>semantic analysis</b> of the code. In this case, the magic happens after the compiler type-checks the loop: <a href="https://github.com/apple/swift/blob/3bd45c81357dc03dc54ee57c567230b147d3a8c8/lib/Sema/TypeCheckStmt.cpp#L716">If we analyze the code from the type-checker</a>, we can see that if the loop represents valid code that iterates a <code>Sequence</code>, it injects a new property called <code>${loopName}$generator</code> that fetches the sequence's iterator, then fetches the iterator's <code>Element</code> type and then finally moves our original closure into a new loop that loops the iterator's <code>next()</code> property.</p>
<p>For example, if we compile the previous loop example, the final result will be something comparable (but not exactly) to this:</p>
<pre><code>let array = [1,2,3]
var $num$generator = array.makeIterator()
while let num = $num$generator.next() {
print(num)
}</code></pre>
<p>The Swift compiler uses the <code>$</code> prefix for internal compiler synthesized properties. Because it needs to inject actual code, it uses a symbol that is impossible to be used in regular code to make sure we won't have namespacing issues. Interestingly, you can actually interact with some of these internal properties as part of the new <b>property wrappers</b> feature.</p>
<h2>What about Collections?</h2>
<p>When studying more about Swift's collection types, <code>IteratorProtocol</code>, <code>Sequence</code> and <code>Collection</code> are often mentioned together, and you might have noticed that the generated SIL for our example mentions <code>Collections</code> as it uses an array. If the syntax sugar is linked to <code>Sequences</code> only, how do collections work?</p>
<p>We can say that a <code>Collection</code> is simply a more jacked-up version of a <code>Sequence</code>. In fact, all <code>Collections</code> are also <code>Sequences</code>:</p>
<pre><code>public protocol Collection: Sequence { ... }</code></pre>
<p>Unlike regular <code>Sequences</code>, <code>Collections</code> allow you to access elements multiple times, iterate them in reverse order (in the case of <code>BidirectionalCollections</code>) and even access specific elements directly through subscripts (in the case of <code>RandomAccessCollections</code> like <code>Array<></code>)</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>Unlike <code>Sequences</code>, <code>Collections</code> do not need to provide an <code>IteratorProtocol</code>. By default, Swift provides the <code>IndexingIterator</code> struct: an <code>IteratorProtocol</code> that takes a collection and simply traverses the indexes that are defined as part of the <code>Collection</code> protocol. As all <code>Collections</code> are <code>Sequences</code>, they can also benefit from having syntax sugar loops.</p>
<p>---</p>
<h2>References and Good reads</h2>
<a href="https://github.com/apple/swift">The Swift Source Code</a>
<br>
</div></div>
<div class="blog-post footer-main">
<div class="footer-logos">
<a href="https://swiftrocks.com/rss.xml"><i class="fa fa-rss"></i></a>
<a href="https://twitter.com/rockbruno_"><i class="fa fa-twitter"></i></a>
<a href="https://github.com/rockbruno"><i class="fa fa-github"></i></a>
</div>
<div class="footer-text">
© 2025 Bruno Rocha
</div>
<div class="footer-text">
<p><a href="https://swiftrocks.com">Home</a> / <a href="blog">See all posts</a></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Blog Post (Right Sidebar) End -->
</div>
</div>
</div>
<!-- All Javascript Plugins -->
<script type="text/javascript" src="js/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script type="text/javascript" src="js/prism4.js"></script>
<!-- Main Javascript File -->
<script type="text/javascript" src="js/scripts30.js"></script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-H8KZTWSQ1R"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-H8KZTWSQ1R');
</script>
</body>
</html>