Hey guys,
I’m the head dev at a startup on the JVM --although I’m doing greenfield fortran dev but that’s another story for later-- and one of the things that is consistently irking me right now is the difficulty of creating and dispatching tasks and futures and the like.
I’m wondering if the Kotlin community has any plans to add Future/Task/Co-routine support with some fancy kotlin-oriented sugar.
Before I go any further, for the record:
- I think the mentality of ‘lets solve all problems’ is currently weakening Scala greatly. I really like Scala, but am so scared of the implicit keyword (among other features) that I haven’t attempted to push it on my company.
- I am very much using this topic as a way to learn more about kotlin, so my current understanding of the language is not exhaustive. I am reasonably well acquainted with the limitations of the JVM however.
Specifically, looking at C#, you’ve got two really cool language features that I’m wondering if kotlin would ever adopt, yield
and await
(and its counterpart async
)
As a previous C# developer, and current java (javafx desktop app) developer, I miss this feature enormously:
void handleUploadButtonClicked(ClickEvent event){
Path file = determineIndicatedFile();
//fast, looks at some text fields or something and creates a java.nio.path object
Object fileContent = await fileSystem.loadContent(file);
//very slow, 'await' = don't hang the UI thread!!
boolean success = await socket.upload(fileContent, file.meta());
//also very slow, but it has to ~block to indicate success or failure.
//Don't hang the UI thread!
if(success) { logSuccess(); }
else { fireStingerUIEffect(); }
}
async Object loadContent(Path file){
//opens channels, acquires file locks, has coffee with the operating system, opens file streams, pulls data off the disk.
//all very slow operations for large files, and thus they cannot be done on the UI thread without usability issues.
}
async boolean upload(Object content, Metadata fileMeta){
//similarly, this is C#-ish pseudo code for a co-rotine based progress-bar friendly
//web-uploading method. Note 'sendPart' could be a traditional blocking call.
do try {
double progress = sendPart(content);
uiProgressBar.setProgress(progress);
}
catch(SessionFailureException e){
return false;
}
while(progress < 1.0);
return true;
}
double sendPart(Object content){
int chunkCount = determinePacketCount(content);
int partNo = 0;
do{
session.transmit(content, partNo, chunkCount);
partNo += 1;
yield partNo / chunkCount;
}while(partNo < totalParts);
yield 1.0;
}
To (very briefly) summarize the purpose of async
, it changes the runtime signature of the method to be Task<Object>
in C# (effectively Future<Object>
in java), with an await (ie getResult()
) method that yields the UI job queue, allowing the UI thread to dispatch other jobs but leaving the effective program counter at the call site, giving the appearance (to the programmer) of a traditional imperative execution flow read: devs don’t have to worry too much about blocking or icky Platform.runLater
calls. On JavaFX this can be implemented with Toolkit#enterNestedEventLoop
. Of course, the corollary is a multi-thread friendly model which we all too often don’t have, but thats a bigger challenge.
I’m wondering if Kotlin can/will adopt anything similar. With existing kotlin syntax (last argument is a lambda) you can get away with:
fun handleUploadButtonClicked(ClickEvent event) {
Path file = determineIndicatedFile();
val fileContent = await { fileSystem.loadContent(file) }
val success = await { socket.upload(fileContent, file.meta()) }
}
which is pretty good, but (afaik) there is no getting around the type-up-conversion from the result type (eg Object
, boolean
) to its corresponding Task (eg Task<Object>
and Task<Boolean>
), so you’re still looking at something like
//method calls a static Task factory
fun loadContent(path: Path) : Task<Object> {
Task.of {
//implementation
}
}
This assumes that there’s useful information to be stored as state on a Task
object, if the only thing we’re after is implementation than we can likely implement everything as extension methods. Of course, the obvious state information is things like isCancelled
and thrownException
or what-have-you.
And regarding Co-routines, I think we’re simply SOL. Again, lamba sugar might get us something, but I dont think it would go very far.
Thoughts? Is there existing cool syntax in kotlin (maybe around infix
or operator
) that I’m missing to get a really slick implementation of long-running non-blocking imperative UI event handlers and/or co-routines?
Thanks for reading!
-Geoff