Image for post
Image for post
Photo by Anne Nygård on Unsplash

The history of programming languages is ripe with evolution. Existing languages constantly evolve and new languages are created to address the emerging needs. Sometimes there are radical, revolutionary breakthroughs, with a complete paradigm shift, but often there are just gradual improvements and refinements. The latter is the topic of this story. The practice of programming at any given era usually goes ahead of capabilities that programming languages provide, while programming language designers recognize it and catch up to fulfill the demand. Let us see some examples to the point.

From GOTOs to the structured code

In early languages, you had to write a lot of repetitive code just to do a simple loop. The loop was such a common programming pattern, that it was adopted even by the primitive higher-level languages in the era predating structured programming. So, there was a time when you still had GOTOs in your programming language but a significant fraction of the code had structured…


Image for post
Image for post
Photo by Davies Designs Studio on Unsplash

Once upon a time coroutines were introduced to Kotlin and they were lightweight. We could launch a multitude of coroutines and we needed a way to communicate between those coroutines without running into a dreaded “mutable shared state” problem.

Thus Channel was added as an inter-coroutine communication primitive. The channels are wonderful. Channels support one-to-one, one-to-many, many-to-one, and many-to-many communication between coroutines, and every value that is sent to the channel is received once.


Image for post
Image for post
Photo by Li Yang on Unsplash

At the dawn of software engineering computers were programmed directly in machine code, then in assembly, and only later in higher-level languages. Computers are imperative. They operate by executing instructions that mutate their state, stored in registers and memory. Naturally, the same was true about programming languages. In the old world of expensive computers with limited resources, the primary concern was an efficient translation of higher-level abstractions into low-level code.

The past glory of mutable state

It used to be a normal practice to write software in a way that mimics the actual computer architecture with thousands of global variables that are being mutated by various pieces of the system. It might be shocking for a modern developer to learn that just recently there were cars on the street, designed as late as 2005, that ran what we would call the “Spaghetti” code. …


Image for post
Image for post
Photo by Ben Wicks on Unsplash

Repetitio est mater studiorum (Latin, repetition is the mother of all learning).

Repetition is great for study, but a bane of software development. Repetitive code is boring and error-prone. Let’s look at a hypothetical example inspired by imperative UI frameworks that are still being used a lot nowadays, even though their heyday is past. We might find ourselves having to write code like this:

applicationWindow.title = "Just an example"
applicationWindow.position = FramePosition.AUTO
applicationWindow.content = createContent()
applicationWindow.show()

Referencing applicationWindow object over and over again is very explicit but it is not pretty. …


Image for post
Image for post
Photo by John Mark Arnold on Unsplash

What are Kotlin Exceptions and how should you use them? To figure it out let’s look at their origins first. Exceptions came to Kotlin from Java. The story with exceptions in Java is complicated, though. I’ll give a brief overview.

The Origin

Java has a unique concept of checked exceptions that were designed to solve the problem of verbose and error-prone error-handling (pun intended). In languages predating Java, like in venerable C, you have to write code like shown in this snippet when doing basic input/output:

file = fopen("file.txt", "r");
if (file == NULL) {
// handle error & return
}
// work with file, check for error after each file…


Image for post
Image for post
Photo by Linus Mimietz on Unsplash

Threads are heavy-weight and have substance. With threads, we can get a reference to some kind of current Thread object, examine its properties, modify its thread-local variables, and otherwise manipulate it. It is no surprise that people with the thread-programming background and education, who are coming to programming with coroutines, are looking for some kind of Coroutine object they can get hold of. However, there is none. Coroutines are phantom, ephemeral, insubstantial. There are good reasons for this state of affairs and some non-trivial consequences. Let’s dig in.

The rule of coroutine transparency

Consider the following suspending function foo that performs some work and then writes the resulting data to a database and sends a message with it over a message bus (both use network and are suspending, too). …


Image for post
Image for post
Photo by Riccardo Pelati on Unsplash

Kotlin Coroutines are typically used for asynchronous programming. However, the underlying design of coroutines and their implementation in Kotlin compiler are quite universal, solving problems beyond asynchronous programming. Let’s take a look at one such problem that can be elegantly solved with coroutines— writing deeply recursive functions.

Setup

Consider a tree data structure. For this example, let’s use this simple binary tree where each Tree node has a reference to its left and right children:

class Tree(val left: Tree?, val right: Tree?)

The depth of the tree is defined as the length of the longest path from its root to its child nodes. …


Image for post
Image for post
DeadEnd by Austin Mulhern

Throughout most of my early professional career, I programmed in languages where you terminate or separate statements with a semicolon; it was like an ideograph distinguishing coders from laypeople. I had a motor reflex to type a semicolon (;) without having to think about it. So when I got involved in the early discussions about the new language that JetBrains was working on back in 2010, the language that would be later called Kotlin, I was not thrilled about the proposal to drop the mandatory semicolons. Who minds the semicolons I thought. I had quite a strong opinion about the need to solve the billion-dollar problem with nulls¹ to make our software safer, but the visuals I cared less about. …


Image for post
Image for post
Vernier Caliper by Michael Brace

When we work on a piece of software, being it an application or a library, we often focus on its functional requirements. They are usually quite easy to directly observe and test; functional requirements are featured in software marketing materials in the form of “feature matrix” that compares different products. Yet, many non-functional aspects of software, also known as quality attributes or qualities for short, such as reliability, efficiency, security, maintainability, usability, etc¹ can be important, too. They are not as easy to measure, though.

Consider, for a example, a task of picking a library to parse some data interchange format. When you face a problem like this, you might have in mind some specific requirements like “it should be able to parse this and that file we have”. You can quickly sketch test code to vet the candidates you’ve found. It is even simpler if the format is formally defined in some standard or standard-like written document. You’ll just read documentation to confirm that the library claims conformance with the corresponding standard and then all you need to do is to perform a straightforward acceptance test. …


Image for post
Image for post
Photo by Annie Spratt on Unsplash

A little over a year ago I announced big conceptual shift in the design of Kotlin Coroutines called Structured Concurrency. From that moment on, it took our team about a month to make the first stable 1.0.0 release of kotlinx.coroutines library. After a year of further work, kotlinx.coroutines had added stable support for cold flows that integrate nicely with reactive streams. The library had reached version 1.3.2 by now. It is good time to look back and see how it all worked out — what was great, what could be improved.

Structured Concurrency accomplished more than we hoped for. Originally, the design of structured concurrency was based on the woes experienced by backend developers trying to implement all sorts of asynchronous and concurrent logic. It was focused on making sure that you cannot ever lose a running coroutine or an exception. The key building block we added back then is coroutineScope { ... } function, which encapsulates concurrent operations and limits their scope to the scope of the current call. …

About

Roman Elizarov

Project Lead for the Kotlin Programming Language @JetBrains

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store