-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
431 lines (384 loc) · 23.5 KB
/
index.html
File metadata and controls
431 lines (384 loc) · 23.5 KB
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
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Functional Python: Practical Paradigms for Readable and Robust Code</title>
<meta name="description" content="Explore functional programming concepts in Python for cleaner, more maintainable code.">
<meta name="author" content="Wenxin Jiang">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="dist/reset.css">
<link rel="stylesheet" href="dist/reveal.css">
<link rel="stylesheet" href="dist/theme/league.css" id="theme">
<link rel="stylesheet" href="dist/theme/black.css" id="theme">
<link rel="stylesheet" href="css/custom.css">
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<h2>Functional Python</h2>
<h3>Practical Paradigms for Readable and Robust Code</h3>
<div style="display: flex; justify-content: center; align-items: center; gap: 4rem; margin-top: 2.5em;">
<img src="img/pycon_logo.svg" alt="PyCon HK Logo" style="height: 160px;">
<div style="text-align: center;margin-left: 2rem;margin-right: 2rem;">
<p style="margin: 0;">Wenxin Jiang, Jian Yin</p>
<p style="margin: 0; font-size: 0.8em;">Oct. 11, 2025</p>
</div>
<img src="img/CityU_logo.svg" alt="CityU Logo" style="height: 120px;">
</div>
<aside class="notes">
Good afternoon, everyone. I'm really happy to meet you here at PyConHK. I'm Jiang Wenxin. This work is done in collaboration with Yin Jian. We are both PhD students at CityUHK.
While object-oriented design gets most of the attention in Python, there's another way of thinking that can make your code clear and reliable. That is functional programming.
Today, I'm going to show you how a few simple ideas from functional programming, like writing predictable functions and keeping immutability in your data, can make your Python code easy to read, simple to test, and a enjoyable to work with.
Let's get started.
</aside>
</section>
<section>
<h2>Who I am</h2>
<div>
<ul>
<li>Wenxin Jiang, Ph.D. Student @ CityUHK</li>
<li>Major in Biostatistics</li>
<li>Research interests: Genetics, High Dimensional Statistics</li>
</ul>
</div>
<aside class="notes">
A bit about myself: I'm Wenxin Jiang, a PhD student at CityUHK. I spend my time working with large genetics datasets to uncover insights in genomics and biology.
In my research, I use both Python and R to analyze complex data.
Like many of you, I'm always searching for ways to write code that's not just correct, but also clear and reliable.
This passion is what drove me to explore functional programming in Python. And I'm truly excited to share what I've discovered with all of you today.
</aside>
</section>
<section>
<h2>An Illustrative Example</h2>
<div style="display: flex; justify-content: center; margin-top: 1em;">
<table style="border-collapse: collapse; width: 70%; box-shadow: 0 4px 6px rgba(0,0,0,0.3); border-radius: 8px; overflow: hidden;">
<thead>
<tr style="background: linear-gradient(135deg, #667eea 0%, #667eea 100%); color: white;">
<th style="padding: 15px 20px; text-align: left; font-weight: 600; font-size: 1.1em;">Name</th>
<th style="padding: 15px 20px; text-align: left; font-weight: 600; font-size: 1.1em;">Class</th>
<th style="padding: 15px 20px; text-align: left; font-weight: 600; font-size: 1.1em;">Subject</th>
<th style="padding: 15px 20px; text-align: left; font-weight: 600; font-size: 1.1em;">Score</th>
</tr>
</thead>
<tbody>
<tr style="background-color: rgba(102, 126, 234, 0.1); transition: all 0.3s ease;">
<td style="padding: 12px 20px; border-bottom: 1px solid rgba(255,255,255,0.1);">Alice</td>
<td style="padding: 12px 20px; border-bottom: 1px solid rgba(255,255,255,0.1);">B01</td>
<td style="padding: 12px 20px; border-bottom: 1px solid rgba(255,255,255,0.1);">Art</td>
<td style="padding: 12px 20px; border-bottom: 1px solid rgba(255,255,255,0.1); font-weight: 600; color: #667eea;">85</td>
</tr>
<tr style="background-color: rgba(118, 75, 162, 0.1); transition: all 0.3s ease;">
<td style="padding: 12px 20px; border-bottom: 1px solid rgba(255,255,255,0.1);">Bob</td>
<td style="padding: 12px 20px; border-bottom: 1px solid rgba(255,255,255,0.1);">B02</td>
<td style="padding: 12px 20px; border-bottom: 1px solid rgba(255,255,255,0.1);">English</td>
<td style="padding: 12px 20px; border-bottom: 1px solid rgba(255,255,255,0.1); font-weight: 600; color: #667eea;">74</td>
</tr>
<tr style="background-color: rgba(118, 75, 162, 0.1); transition: all 0.3s ease;">
<td style="padding: 12px 20px; border-bottom: 1px solid rgba(255,255,255,0.1);">Charlie</td>
<td style="padding: 12px 20px; border-bottom: 1px solid rgba(255,255,255,0.1);">A03</td>
<td style="padding: 12px 20px; border-bottom: 1px solid rgba(255,255,255,0.1);">Art</td>
<td style="padding: 12px 20px; border-bottom: 1px solid rgba(255,255,255,0.1); font-weight: 600; color: #eb2a2a;">Error</td>
</tr>
<tr style="background-color: rgba(102, 126, 234, 0.1); transition: all 0.3s ease;">
<td style="padding: 12px 20px;">...</td>
<td style="padding: 12px 20px;">...</td>
<td style="padding: 12px 20px;">...</td>
<td style="padding: 12px 20px;">...</td>
</tr>
</tbody>
</table>
</div>
<aside class="notes">
Let's start with a simple example: a table of student scores. Each row contains a student's name, class, subject, and their score.
But we notice that one entry has Error indicating the data was missing or corrupted.
This kind of issue is very common in real-world data. Sometimes they are NA, or even wrong types like strings instead of numbers.
In real projects, I believe most of us need to start our analysis with such messy datasets. Before diving into tasks like calculating grades, or finding averages for each subject or class, we need to carefully clean the data and handle errors first.
</aside>
</section>
<section>
<h2>Imperative Style: How To Do</h2>
<div>
<img src="img/illu_imperative.png" alt="Imperative Code Example" style="width: 70%; box-shadow: 0 4px 6px rgba(0,0,0,0.3); border-radius: 8px;"/>
</div>
<aside class="notes">
This is imperative style, don't need to read through this. It's just an example to illustrate the ideas.
This is the traditional way, and the key point is that it's very detailed and step-by-step.
In imperative style, we tell the computer exactly how to do each task. For example, we write a for loop to process each row, use try-except to handle errors, and rely on if-elif chains to calculate grades. We also manually update statistics step by step.
It works, but it's long and hard to follow, especially as things get more complex. You have to read through all the details to understand the overall logic. I always get lost in such code.
</aside>
</section>
<section>
<h2>FP Style: What To Do</h2>
<div>
<img src="img/illu_fp.png" alt="Functional Code Example" style="width: 70%; box-shadow: 0 4px 6px rgba(0,0,0,0.3); border-radius: 8px;"/>
</div>
<aside class="notes">
Now, with the functional approach, we can focus directly on what our goal is, without worrying about the details of how to achieve it.
We pack each step into a small simple function. And each function handle a specific task, such as adding grades, filtering scores, or calculating averages.
Linking these functions together using methods like pipe, we create a clear and logical pipeline.
This makes the main code short, readable, easy to follow, and simple to test and modify.
</aside>
</section>
<section>
<h2>What is FP?</h2>
<p>Core: <strong>Pure Functions</strong></p>
<ul>
<li>Functions with <strong>no side effects</strong></li>
<li>Avoid global state</li>
<li>Same result for the same input</li>
<li>One function, one task</li>
</ul>
<aside class="notes">
The core principle of functional programming is function and especially pure function.
A pure function is one that does not have side effects. This means it doesn't change any data outside of itself.
We also avoid using global variables that can be changed from anywhere. This keeps our functions isolated and predictable.
And, it's like a simple math equation: it takes some input, gives you an output. And if you give it the same input again, it will always return the same output.
Each function should focus on doing just one thing well. Pure functions make your code predictable and easy to test and maintain.
</aside>
</section>
<section>
<h2>What is FP?</h2>
<div style="display: flex; justify-content: center; margin-top: 0em;">
<table style="border-collapse: collapse; width: 80%; box-shadow: 0 4px 6px rgba(0,0,0,0.3); border-radius: 8px; overflow: hidden; font-size: 0.85em;">
<thead>
<tr style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
<th style="padding: 15px 20px; text-align: center; font-weight: 600; width: 33%;">Aspect</th>
<th style="padding: 15px 20px; text-align: center; font-weight: 600; width: 33%;">Functions</th>
<th style="padding: 15px 20px; text-align: center; font-weight: 600; width: 33%;">Variables</th>
</tr>
</thead>
<tbody>
<tr style="background-color: rgba(102, 126, 234, 0.15);">
<td style="padding: 15px 20px; text-align: center; font-weight: 600; color: #667eea;">Principle</td>
<td style="padding: 15px 20px; text-align: center;">
<strong style="font-size: 1.1em;">No Side Effects</strong>
</td>
<td style="padding: 15px 20px; text-align: center;">
<strong style="font-size: 1.1em;">Immutability</strong>, Avoid Global State
</td>
</tr>
<tr style="background-color: rgba(118, 75, 162, 0.1);">
<td style="padding: 15px 20px; text-align: center; font-weight: 600; color: #667eea;">Meaning</td>
<td style="padding: 15px 20px; text-align: center;">
Output depends<br/>only on input
</td>
<td style="padding: 15px 20px; text-align: center;">
Data cannot be<br/>modified
</td>
</tr>
<tr style="background-color: rgba(102, 126, 234, 0.15);">
<td style="padding: 15px 20px; text-align: center; font-weight: 600; color: #667eea;">Result</td>
<td style="padding: 15px 20px; text-align: center;">
Predictable &<br/>Testable
</td>
<td style="padding: 15px 20px; text-align: center;">
Safe &<br/>Thread-safe
</td>
</tr>
</tbody>
</table>
</div>
<aside class="notes">
This table highlights the two key principles of functional programming.
On the function side, we want no side effects. Functions shouldn't unexpectedly change any other parts of your program. On the variable side, we want immutability. Data shouldn't be changed once created. These principles are connected: if your data is immutable, your functions naturally avoid side effects, because they can't modify the data.
As I will mention in my another talk, recent Python versions now support true parallelism, which is a really exciting development. Functional programming helps here too, by ensuring code stability in multi-threaded tasks.
</aside>
</section>
<section>
<h2>Immutability: The Problem</h2>
<p style="color: #eb2a2a;">❌ Mutable Approach</p>
<pre><code class="python">scores = [85, 74, 60] # Shared mutable state
def add_bonus(score_list):
for i in range(len(score_list)):
if i % 2: # Only add bonus to odd index
score_list[i] += 10 # Modifies original!
return score_list
original_scores = scores
bonus_scores = add_bonus(scores)
print(original_scores) # Output: [85, 84, 60] 😱 Changed!</code></pre>
<p style="color: #eb2a2a; margin-top: 5px; font-size: 1em;">⚠️ Unexpected mutation causes bugs!</p>
<aside class="notes">
As I have mentioned, immutability is a key idea in functional programming.
But why does it matter so much? What's the problem with ignoring immutability?
Let me show you a common pitfall. Imagine a simple task that we want to add a bonus to some of scores. We have a original scores, and we have bonus scores after adding 10 points.
But this function somehow changes the original list! Because two score variables point to the same list in memory. Some students' scores might get an unexpected bonus, which is totally unfair!
</aside>
</section>
<section>
<h2>Immutability: The Solution</h2>
<p style="color: #4ade80;">✅ Immutable Approach</p>
<pre><code class="python">scores = [85, 74, 60] # Immutable approach
def add_bonus(score_list):
# Create new list, don't modify original
return [score + 10 if i % 2 else score \
for i, score in enumerate(score_list)]
original_scores = scores
bonus_scores = add_bonus(scores)
print(original_scores)
# Output: [85, 74, 60] ✓ Unchanged!</code></pre>
<p style="color: #4ade80; margin-top: 5px; font-size: 1em;">✓ Original data is safe!</p>
<aside class="notes">
Now let's look at the functional solution.
Instead of changing the original data, here in this function, we create a brand new list with the updated values. The original stays safe and unchanged.
This is why functional programming gives me greater confidence in my code. Without it, I've often spent hours tracking down bugs caused by some other function hundreds of lines away. When I found it, it was like a Goblin suddenly jump out and say "Surprise! I changed your data!".
</aside>
</section>
<section>
<h2>Why FP?</h2>
<ul>
<li><strong>Readability:</strong> Focus on "what" rather than "how"</li>
<li><strong>Modularity:</strong> Small, isolated functions</li>
<li><strong>Robustness/Testability:</strong> Easier to test</li>
</ul>
<aside class="notes">
So why choose functional programming?
First, readability: focus on what you want, not how to do it. It's like giving a command to come to CityU without listing every detail, like go to Kowloon Tong, then Festival Walk.
Second, modularity: small functions act like logo blocks. You can build complex behavior by combining simple, well-defined pieces. Each function does one thing, and does it well.
Third, testability: pure functions are predictable and isolated. You can easily write tests for them without worrying about the rest of your program.
In the end, it's about writing code that's easier to read, test, and maintain. No surprises, no getting lost.
</aside>
</section>
<section data-visibility="hidden">
<h2 style="font-size: 1.2em;">Currying & Higher-Order Functions</h2>
<img src="img/currying.jpg" alt="Currying Example" style="width: 70%; box-shadow: 0 4px 6px rgba(0,0,0,0.3); border-radius: 8px;"/>
<p style="font-size: 0.5em; margin-top: 0pt;">Source: <a href="https://www.linkedin.com/pulse/currying-takes-your-functions-whole-new-level-power-abdul-ghaffar/">Currying takes your functions to a whole new level of flexibility and power!</a></p>
<aside class="notes">
xx This image shows the key difference: uncurrying is the normal way - passing all arguments at once like f(a, b, c).
Currying breaks this into steps: f(a)(b)(c). Each call takes one argument and returns a new function.
Why does this matter? It lets you create partial functions. You can fix some arguments early and reuse the function in
different ways. This makes your code more flexible and modular.
</aside>
</section>
<section>
<h2>Pipeline Style</h2>
<div style="margin-top: 1em;">
<!-- <p style="margin-bottom: 0.6em;text-align: left;">Math to Code: </p> -->
<p style="font-size: 1em; margin-bottom: 1em;">
$h(g(f(x))) = (h \circ g \circ f)(x)$
</p>
<div style="font-size: 0.8em; margin-left: 2em;">
<div style="margin-bottom: 0em;">
<p style="color: #eb2a2a; margin-bottom: 0.5em; text-align: left;">❌ Nested (Hard to read)</p>
<pre><code class="python" style="font-size: 1.2em;">result = h(g(f(df), arg1=a), arg2=b)</code></pre>
</div>
<div>
<p style="color: #4ade80; margin-bottom: 0.5em; text-align: left;">✅ Pipeline (Clear flow)</p>
<pre><code class="python" style="font-size: 1.2em;">result = df.pipe(f).pipe(g, arg1=a).pipe(h, arg2=b)</code></pre>
</div>
</div>
</div>
<aside class="notes">
Well. If you are familiar with R language, you must know the pipe operator. It make data analysis code super clean and easy to read. If not, no worries, let me inspire you with a mathematical perspective on function composition.
There are two common approaches: the traditional nested style and the pipeline style.
Nested code is like a maze — you have to find and start at the innermost function and work your way outward. It's really easy to get lost.
The pipeline style, on the other hand, tells a much more straightforward story: first apply f, then pipe to g with argument a, then to h with b. The flow is simple, clean, and easy to follow.
</aside>
</section>
<section>
<h2>Railway Analogy for Function Composition</h2>
<div>
<img src="img/railway_func_combine.png" alt="Railway Pattern" style="width: 70%; background-color: white; box-shadow: 0 4px 6px rgba(255, 255, 255, 0.3); border-radius: 8px;"/>
</div>
<p style="font-size: 0.5em; margin-top: 0pt;">Source: <a href="https://speakerdeck.com/swlaschin/railway-oriented-programming-a-functional-approach-to-error-handling?">Railway Oriented Programming</a></p>
<aside class="notes">
Another analogy for function composition is railway tracks.
Imagine building a railway system where each piece of track serves a specific purpose.
Functional programming is a practical way to build complex behavior from small, reliable pieces.
Think of each function as a small piece of track: one turns a pineapple into an apple, another turns an apple into a banana.
We can compose them because the output of one function matches the input of the next.
Each piece is small, testable, and reusable. When requirements change, we can simply add or adjust some functions without rewriting the entire track.
</aside>
</section>
<section>
<h2>Railway Analogy for Error Handling</h2>
<div>
<img src="img/railway_two_track.png" alt="Railway Pattern Code Example" style="width: 70%; background-color: white; box-shadow: 0 4px 6px rgba(255, 255, 255, 0.3); border-radius: 8px;"/>
</div>
<p style="font-size: 0.5em; margin-top: 0pt;">Source: <a href="https://speakerdeck.com/swlaschin/railway-oriented-programming-a-functional-approach-to-error-handling?">Railway Oriented Programming</a></p>
<aside class="notes">
Now let's add the second track to handle errors. Top green track for normal values, bottom red track for errors.
Each function acts like a switch: if it succeeds, the flow continues on the green track; if it fails, we drop to the red track, bypassing the rest of the pipeline.
In Python, we can raise an exception automatically redirects the flow to the nearest except block — that's our red track.
We write small, composable steps where each function either returns a value or raises a specific error. All errors are then handled in one centralized place.
Therefore, our main pipeline stays clean and focused on the happy green path. And we don't need nested try-except blocks everywhere.
</aside>
</section>
<section>
<h2>Other Interesting Concepts in FP</h2>
<div style="font-size: 0.8em; margin-top: 1em;">
<ul>
<li><strong>Iterators:</strong> <code>for i in iter_obj:</code></li>
<li><strong>Map/Filter/Reduce:</strong> <code>map(abs, [-1, 1])</code></li>
<li><strong>List Comprehensions:</strong> <code>[x for x in range(9) if x % 2]</code></li>
<li><strong>Generators:</strong> Use <code>yield</code> to produce a sequence of values lazily.</li>
<li><strong>Lazy Evaluation:</strong> Delay computation until necessary, improving performance.</li>
</ul>
</div>
<p style="font-size:0.8em; margin-top: 1em;">
Docs: <a href="https://docs.python.org/3/howto/functional.html#" target="_blank">Functional Programming HOWTO</a>
</p>
<aside class="notes">
There are also some other useful functional concepts in Python that can makes our life easier. Here's a brief overview.
Iterators let you loop over data without loading it all into memory.
Map, filter, and reduce allow you to apply functions to collections in a clean and efficient way.
List comprehensions provide a concise way to create lists.
Generators allow you to produce sequences of values on the fly.
And lazy evaluation delays computation until it's really needed, which can boost performance.
</aside>
</section>
<section>
<h2>Thank you for attention!</h2>
<ul>
<li>Wenxin Jiang</li>
<li>GitHub: <a href="https://github.com/LucaJiang" target="_blank">LucaJiang</a></li>
<li>This presentation: <a
href="https://lucajiang.github.io/functional_python/">lucajiang.github.io/functional_python/</a></li>
</ul>
<aside class="notes">
Thank you for your attention. You can find me on GitHub at LucaJiang, and the slides of this talk are available online.
</aside>
</section>
</div>
</div>
<script src="dist/reveal.js"></script>
<script src="plugin/math/math.js"></script>
<link rel="stylesheet" href="plugin/highlight/github-dark.css" id="highlight-theme" />
<script src="plugin/highlight/highlight.js"></script>
<script src="plugin/notes/notes.js"></script>
<script src="plugin/menu/menu.js"></script>
<script>
Reveal.initialize({
controls: true,
progress: true,
center: true,
hash: true,
// disableLayout: true,
transition: 'none',
slideNumber: true,
showSlideNumber: 'all',
help: true,
mathjax3: {
mathjax: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js',
tex: {
inlineMath: [
['$', '$'],
['\\(', '\\)'],
],
},
options: {
skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre'],
},
},
menu: {
titleSelector: 'h1, h2, h3',
hideMissingTitles: true,
},
plugins: [RevealMath.MathJax3, RevealHighlight, RevealNotes, RevealMenu],
// plugins: [RevealMath.MathJax3, RevealHighlight, RevealNotes],
});
</script>
</body>
</html>