Tales from the jar side: Coroutines, Extension Functions, and Managing Your Manager
This newsletter is supposed to be about technical issues that came up during the week, and this week it actually kind of is.
Welcome to Tales from the jar side, the Kousen IT newsletter, for the week of June 21 - 28, 2020. This week I taught O’Reilly courses on Kotlin Fundamentals and Functional Java. I also worked on my Managing Your Manager book, which grows ever closer to a beta release.
Down the Coroutines Rabbit Hole
Geek stuff first. My Kotlin class was fun, as usual. I’m getting better at anticipating the sorts of questions that come up. The only problem I had was falling down a technical rabbit hole.*
*Got to watch out for those technical rabbits. They breed like, well, you know.
On the Kotlin home page, there is a section called “Try Kotlin” with three examples: one trivial “Hello, World!” sample, one object-oriented version of the same, and one on coroutines. I generally like to show all three as we start the class. The first two are very simple. The last one involves coroutines, and is not simple.
Maybe that’s debatable. As coroutines code goes, it’s not bad. The problem with coroutines, though, is that there are a lot of moving parts, and asynchronous coding is hard enough no matter what library you’re using.
For the record, coroutines are “lightweight threads” (whatever those are) that allow you to run many processes concurrently using code that avoids complicated call-back methods. While you’re limited to a certain number of threads, you can launch 100,000 coroutines and not run out of memory.
Yes, those statements need lots of explanations, but just go along with it for the moment.
Here’s the actual sample from that page. It’s only eight lines of code:
suspend fun main() = coroutineScope {
for (i in 0 until 10) {
launch {
delay(1000L - i * 10)
print("❤️$i ")
}
}
}
If you already know the API, that’s not bad. If you’re a beginner to Kotlin, you need to know:
A suspend function is one that can be interrupted and resumed by the runtime environment.
If your implementation is a single statement, you can use an equals sign to assign the signature to the expression on the right-hand side, whose evaluation is the return type of the function. That’s a very common idiom in the Kotlin library.
The term coroutineScope is actually a function that takes a lambda expression as an argument.
In Kotlin, if you call a function where the last argument is a lambda expression, you can put the lambda after the parentheses. If the only argument is a lambda, you can then leave out the parentheses, which was done here.
The argument to the coroutineScope function is actually a lambda with a receiver. The lambda takes no arguments and returns a generic type, but operates in the context of a class called CoroutineScope (note the capital letter, as opposed to the lowercase first letter in the function call). All that means the runtime searches in the CoroutineScope class when it needs to resolve functions or properties that are not defined locally.
One of those is the launch function, which launches coroutines (I know, right? What were the odds?). Oh, and its argument is also a lambda with receiver, but this time the receiver isn’t directly relevant.
The lambda block contains a delay function call, which is a top-level suspend function that waits for a certain number of milliseconds (as a Long argument — note the L on 1000) before moving on.
Each coroutine delays an increasing number of milliseconds before launching, at which point it prints a heart (Holy Unicode, Batman!) and a space.
All of that is wrapped in a for-in loop that uses an open IntRange. The IntRange consists of the numbers 0 through 9, because until is an infix function (which is why it doesn’t need a dot or parentheses) on Int that counts up to, but not including, its argument.
The result is: ❤️9 ❤️8 ❤️7 ❤️6 ❤️5 ❤️4 ❤️3 ❤️2 ❤️1 ❤️0
Got all that? Remember, this is the simple coroutines example on the Kotlin home page. We really haven’t learned much by this point. Normally I can gloss over the details as we go, but this time I had over 100 students (which is good) and way too many of them were fascinated by coroutines.
Because I always feel an unholy compulsion to answer every question that’s asked of me in class, we wound up spending way more time on that example than I intended. The discussion ranged from implementation details under the hood (which I don’t know or really care about) to comparisons with reactive libraries based on the reactive streams specification or promises in JavaScript or even CompletableFuture in Java. I managed to move on, but it was tougher than usual, because, after all, they were good questions and coroutines is one of the biggest selling points of the language.
Only one person actually said privately in the Q&A widget something like, “maybe we shouldn’t be digging so deeply into coroutines at the beginning of the course?” and I was forced to agree. As I say, usually I can manage that, but this time was harder than normal. Fortunately, I managed to cover everything during the class that I intended to, so I’m putting it in the win column anyway. No harm, no foul.
Generic Extension Functions
During the class, I also realized a function I’d written could be made into a generic extension function. One example I like to do in class is where I show the number of astronauts currently in space, based on a simple rest service located at Open Notify. I use the Kotlin extension function readText added to Java’s URL class to download the data, and Google’s Gson library to parse it. That code looks like:
import com.google.gson.Gson
import java.net.URL
data class Assignment(
val name: String,
val craft: String)
data class AstroData(
val message: String,
val number: Int,
val people: List<Assignment>)
fun main() = Gson().fromJson(
URL("http://api.open-notify.org/astros.json").readText(),
AstroData::class.java
).let {
println("There are ${it.number} people in space")
for ((n, c) in it.people) {
println("$n aboard $c")
}
}
The weird part (assuming it’s not all weird) is the AstroData::class.java line. The point I make in class is that the Gson library doesn’t know anything about Kotlin. Its fromJson method takes a JSON string as the first argument and a Java class as the second argument. The strange syntax shown is how you can get the Java class from the Kotlin KClass. It all works, and as of today the answer is:
There are 5 people in space
Chris Cassidy aboard ISS
Anatoly Ivanishin aboard ISS
Ivan Vagner aboard ISS
Doug Hurley aboard ISS
Bob Behnken aboard ISS
That’s all well and good, but it occurred to me I can change the call to fromJson using an extension function:
inline fun <reified T> Gson.fromJson(json: String): T =
this.fromJson(json, T::class.java)
That reduces the fromJson call to just
Gson().fromJson<AstroData>(
URL("http://api.open-notify.org/astros.json").readText())
This means I created a version of fromJson that just delegates to the one that already exists. The question I have is, is this worth doing in the first place? Do I gain anything by writing the extension function, or should I just leave the original call as it is?
I tried asking that question on the Kotlin Slack channel, and actually got a quick reply from Jake Wharton himself, who said:
“You would need to use typeOf or else things like List and Map and other generic types will be broken.”
Well, maybe. I tried switching to typeOf<T>.javaType, but that gave me errors I couldn’t resolve. Also, that doesn’t really answer my question.
The thing is, Jake Wharton is one of the Truly Big Names in the Kotlin world, so the last thing I wanted to do is argue with him. When I started digging into it, I found that Jake is not only the creator of the OkHttp and Retrofit libraries, he’s also one of the contributors to the Moshi library (like Gson, but different), so all the more reason not to debate him. I have to admit, though, that I’m not wild about that library (it’s more complicated than Gson, and I don’t see a benefit for simple cases). So after digging a while, I let it go.
What does this all mean? It means I should have just asked my friend Tim Yates in the first place.
(Of course, I effectively just did. :)
Functional Java
This class was fun, too, but for different reasons. Again it was well-attended, which still makes me happy but mystifies me. The original name of this course was Java 8 Upgrade, because the functional features we talk about were almost all added in Java 8, which was released in March of 2014, practically ancient history in this world. I’ve written two books since then, one called Modern Java Recipes that deals directly with this stuff. It’s also a two-day class on the O’Reilly Learning Platform, which is unusual these days. Still, every time I run it, we get a big turnout. Cool, but every time I wonder how long that will last.
I didn’t do anything significantly different this time than I normally do, so I won’t add yet even more code this newsletter. I’ll just say I got several good questions and a good time was had by all (or at least by those who bothered to comment), and I’ll settle for that.
Selling Managing Your Manager
As we approach the beta release of my Managing Your Manager book, I’m supposed to fill out a file that gives descriptions of the book of various lengths, along with “feature points” and “key selling points” that will convince people to read it.
Ugh. Look, here’s the idea behind my book:
As a working professional, you care a lot about the quality of your work. You want to do things the right way, which isn’t always the quickest or simplest or least expensive way, at least not in the short run. You want to build something you can be proud of, and do your job to the best of your ability. If it makes money for the company, so much the better, but you really want it to be right, and even elegant or beautiful is possible.
Managers, however, care more by financial issues than you do, because that’s how they are rewarded. In most cases, they can’t even do your job, especially if you are a specialist, yet they are making decisions about your job and your career. As a result, conflict is inevitable.
The goal of my book is to help you build a relationship with your manager that works for both sides. You give them the support and loyalty they need, and they give you the freedom and resources you need to grow and succeed. I call that constructive loyalty, and it’s the basis of getting you what you want when you want it.
Fortunately, most of the techniques I recommend in the book are straightforward do’s and don’ts, like giving good enough answers, expressing the concepts of “I got this,” and “I got your back” in a productive way, avoiding violating the chain of command unnecessarily, and so on. I also give a technique based on solutions to the iterated prisoner’s dilemma problem to hopefully train your manager over time to take your priorities into account when they’re making decisions.
In short, I call this “winning the game of business, from the employee side.” I think it’s valuable. It won’t always work, but it gives you a framework to try so that when the inevitable conflicts arise, you don’t feel your only choices are either to go along and swallow your objections, or to leave and start over somewhere else.
Somehow, in the next day or so, I need to translate that explanation into descriptions of various lengths, feature points, key selling points, market information, and so on. I’m struggling with that, which is part of why I decided to explain it all here. I figure that’s got to help, and you can always skip this section if you’re bored. :)
When the book does go out for reviews, I think this cartoon will be highly relevant:
The Rest of the World
A couple of relevant figures this week:
Lovely. Who knew that politicians telling a large group of people to actively opposing wearing masks in public would lead to a problem? Other than basically everybody, I mean. There’s good news, however (for a limited definition of good):
See that tiny region of green in the upper right? That’s Connecticut (and Rhode Island, I guess, but whatever), where I live. Yay. I’m sure we’ll be fine, for however how long that lasts.
A lot more went on this week, mostly difficult, but I’m not going to go into it here. My own observation was that people online were more on edge this week — angrier, more frustrated, more impatient. There’s a lot of pain going around.
I suggest keeping that in mind when you feel the need to respond. People often say things online that they would never say in real life. My own approach is to try to offer encouragement, or at least solace, where I can. We’ll see how long that lasts, too.
And yes, I must admit that the air is minty fresh and clean up here on the moral high ground. I’ll dismount from my high horse now.
Last week:
Kotlin Fundamentals, on the O’Reilly Learning Platform
Functional Java, same
Working on Managing Your Manager
This week:
Reactive Spring, on the O’Reilly Learning Platform
Working on Managing Your Manager