Recent Posts
Archives

Posts Tagged ‘Readability’

PostHeaderIcon [PyConUS2025] Or Else: Exploring Lesser-Known Control-Flow Constructs in Python

Lecturer

Amethyst Reese is a senior software engineer specializing in foundational infrastructure and developer tooling. Professional Python work began in 2010; subsequent roles have included production engineering at Meta and senior engineering at Astral on the Ruff linter. Reese maintains multiple open-source libraries under the omnilib organization and speaks regularly at Python conferences. Personal site: https://amethyst.cat; GitHub: amyreese.

Abstract

Amethyst Reese examines control-flow mechanisms that lie beyond the familiar if, for, and while constructs. The talk surveys the else clauses attached to try, for, and while statements, demonstrates how decorators and generators can be repurposed to invent new control structures, and illustrates the resulting techniques with a compact text-adventure engine built entirely from generator-based state machines. Throughout, the guiding criterion remains readability: syntactic novelty is justified only when it renders intent more obvious to future maintainers.

Fundamental Alternatives and the Versatile Else Clause

Control flow comprises any language feature that alters the sequential execution of statements. The elementary building blocks—conditionals, loops, and function calls—can express every higher-order pattern, yet Python supplies additional syntactic forms that make certain patterns more concise. List, set, and dictionary comprehensions are the most widely used; they collapse a for loop and optional filtering into a single expression. When the expression grows nested or multi-clause, however, the gain in compactness may be offset by a loss of transparency. Multi-line comprehensions can conceal cyclomatic complexity that would be visually apparent in an equivalent nested loop, leading maintainers to underestimate performance cost. Reese therefore urges developers to weigh not only runtime efficiency but also the cognitive load imposed on the next reader.

The else clause, ordinarily associated with if, appears in three additional contexts. Inside a try statement an else block executes only when no exception was raised, providing a natural location for success-path logic while keeping the try suite minimal. Unlike finally, a return inside the try suite bypasses else; critical cleanup must therefore remain in finally or be placed after the entire construct. In a for loop the else suite runs if and only if the iterator was exhausted without encountering a break—exactly the situation in which a search has failed to locate its target. The same semantics apply to while: else executes when the loop condition becomes false rather than when a break occurs. In each case the language supplies, free of charge, a Boolean flag that programmers would otherwise maintain manually. The resulting code is both shorter and more declarative.

Decorators, Generators, and Custom Control Structures

Decorators are themselves a control-flow device: the @ syntax is executed at definition time, replacing the original object with whatever the decorator returns. Most decorators wrap the original callable, inserting logging, timing, or authorization checks. Because the decorator body runs immediately, it can also perform side effects or even begin iteration. Reese exploits this property to reconstruct two control structures familiar from other languages. An “until” decorator repeatedly invokes its wrapped function until a supplied predicate becomes true, thereby inverting the sense of a while loop. A more elaborate “do” / “while” pair implements the classic do-while pattern: the body always executes at least once, after which the predicate is evaluated. The published do-while package on PyPI encapsulates the necessary bookkeeping, supporting both callables and truthy collections as termination conditions.

Generators supply a second, more powerful substrate. A function containing yield is suspended at each yield point and may later be resumed; values can be sent inward via the send method and exceptions injected via throw. These primitives enable cooperative multitasking and, more relevantly, explicit state machines. Reese constructs a minimal scheduler that treats each generator as a state. Yielding the name of another generator function effects a transition; the target generator resumes exactly where it previously left off. Shared mutable state can be passed among the generators, allowing them to coordinate. The technique is demonstrated by a text-adventure game whose rooms are individual generator functions. Player commands are read inside a room, game state is updated, and a different room function is yielded to move the player. The entire engine occupies fewer than thirty lines; the complete adventure, including all rooms and logic, remains under two hundred lines.

The closing counsel is conservative. Novel control-flow constructs should be introduced only when they render the author’s intention clearer than the equivalent expansion into elementary forms. “Clear is better than clever.” Decorators that invert loop conditions or generators that encode state machines can satisfy that test, yet they also risk becoming opaque to colleagues unfamiliar with the idiom. Compassion for future readers—including one’s future self—remains the ultimate design constraint.

Links:

PostHeaderIcon [KotlinConf2019] Putting Down the Golden Hammer: Pragmatic Kotlin with Huyen Tue Dao

Learning a new programming language, especially one as rich and expressive as Kotlin, often comes with the temptation to use every new feature and syntactic sugar—the “golden hammer”—everywhere. Huyen Tue Dao, a prominent Android developer then known for her work at Trello and her insightful contributions to the Kotlin community, addressed this phenomenon at KotlinConf 2019. Her talk, “Putting Down the Golden Hammer,” shared valuable lessons learned from nearly three years of full-time Kotlin development, emphasizing the importance of pragmatism, readability, and maintainability over merely clever code. Huyen’s insights can often be found on her blog, randomlytyping.com.

Huyen began by acknowledging the allure of Kotlin’s advanced features. While these constructs can be powerful and lead to concise code, she cautioned that their overuse or misuse can sometimes negatively impact code clarity and long-term maintainability. The journey of a developer learning Kotlin often involves an initial phase of enthusiasm for these new tools, followed by a more mature understanding of when and how to apply them judiciously.

The Evolution of a Kotlin Developer’s Approach

Huyen Tue Dao shared her personal evolution in approaching various Kotlin features. She reflected on how code written early in her Kotlin journey might look different from her current practices, shaped by experience, trial, and error. This evolution is natural and signifies a deeper understanding of the language’s nuances and the trade-offs involved in different coding styles.

One key area she touched upon (common in such discussions) is the balance between conciseness and readability. Kotlin allows for very compact code, but sometimes a slightly more verbose approach can be easier for others (and one’s future self) to understand and maintain. She stressed that while Kotlin’s features are designed to make developers more productive, this productivity should not come at the expense of creating code that is difficult to reason about or debug. The goal should be to write “better Kotlin,” which implies code that is not only functionally correct and efficient but also clear, pragmatic, and maintainable.

Pragmatism in Feature Usage: Scope Functions, Nullability, and More

Huyen Tue Dao delved into specific Kotlin features and utilities, discussing both their “fun and shiny” aspects and how they can be overused or even abused. For instance, scope functions (let, run, with, apply, also) are powerful tools for executing a block of code within the context of an object. However, excessive nesting or unclear usage can lead to code that is hard to follow. Huyen advocated for intentionality: choosing the right scope function for the task and ensuring its use enhances clarity rather than obscuring it.

Null safety is another cornerstone of Kotlin, designed to prevent NullPointerExceptions. Huyen discussed the importance of being deliberate about nullability. While Kotlin’s type system helps manage nulls effectively, developers still need to make conscious decisions about whether a type should be nullable and handle nullable types appropriately, without resorting to excessive safe calls (?.) or non-null asserted calls (!!) that might hide underlying design issues. She emphasized being clear about why something is nullable and documenting such decisions when necessary, especially when assumptions about nullability change or when interacting with APIs where null safety guarantees might be different.

Towards Readable and Maintainable Kotlin

Ultimately, Huyen Tue Dao’s message was a call for mindful Kotlin development. She encouraged the audience to revel in Kotlin’s wonderful syntax and features but to always keep readability, maintainability, and pragmatism at the forefront. Understanding the underlying implementation of certain features can also be crucial to avoid potential performance issues.

The aim is not to write code that merely feels “sexy and Kotlin-y” but to craft solutions that are robust, easy for a team to work with, and stand the test of time. Recognizing the potential pitfalls of overusing certain constructs and consciously choosing simpler, clearer alternatives when appropriate leads to what Huyen termed “even better and even more fun Kotlin”. Her talk served as a valuable reminder that true mastery of a language lies not just in knowing its features, but in knowing when and how to use them wisely.

Links: