Scripting in Kotlin with kscript
I had a side-project where I wanted to extract some functionality as a script. The project was an app built with Kotlin and Spring Boot. My initial notion was to use one of the more common languages for these types of tasks such as Python or Ruby.
While I have dabbled in Groovy somewhat, scripting and the JVM doesn’t jump to mind when thinking about a good developer experience. It’s not impossible, but I don’t consider it ideal when all I want to do is to put a piece of code into a file and run it from the terminal.
That being said, I like Kotlin. There are several reasons, but one of them is the fact that it’s concise. Concise enough that writing scripts in it did not seem like a terrible idea (and the code I wanted to extract was already written in Kotlin, so there’s that).
And so, the research into using Kotlin for scripting began.
The two choices
After some time spent researching, the choices were narrowed down to two contending options:
- Using the built-in native scripting support
- Using kscript, written by Holger Brandl
I won’t go into the details surrounding the pros and cons of both approaches, I think the posts of Martin Bonnin and Daniele Bonaldo do a good job explaining them. Holger Brandls talk from KotlinConf 2017 also contains a section about the differences between the two.
With the fresh release of Kotlin 1.4, some parts mention the scripting support, but at the time of this writing I haven’t looked into this.
In the end I made the choice of using kscript because it seemed more feature-rich and easier to use.
Setup
Kscript is installable with sdkman, making the setup fairly easy. You need Maven and Kotlin installed prior.
Installing kscript then becomes: sdk install kscript
After that, you’re basically good to go.
The developer experience
IDEA support built-in
One of the my favourite features by far is how kscript integrates with IDEA. One flag --idea
, and you’re suddenly in familiar territory.
IntelliSense, auto-completion, and seamless transition between the command line and the IDE. The only drawback was that the familiar
Git integration and option to download JavaDocs from third party libraries was missing. I didn’t spend much time looking into this, so I’m not blaming
that one on kscript.
Fixing annotation-based dependency resolution in IDEA
I had some troubles finding the documentation for setting up annotation-based support in IDEA initially. This won’t impact running the script, but IDEA will complain
about unrecognised annotations. Adding the maven artifact com.github.holgerbrandl:kscript-annotations:1.2
manually into the generated IDEA project solved it
(entry is at the bottom of the list):
The annotation dependency is automatically added if kscript sees any of kscript-related annotations within the files. This setup only removes the errors displayed in IDEA. The script will run just fine in the terminal regardless.
Speed of execution
Next up is the speed. Since kscript caches the MD5 checksum of each script (slide from KotlinConf 2017 presentation showcasing it), it only needs to recompile the modified files and pull in external dependencies that’s not present in the cache.
I did have some issues early on with a library resolution, but it was solved using kscript --clear-cache
. After that it never reappeared.
No private methods?
I could not get to the bottom of this, but marking any function in scripts as private
threw an exception each time I attempted it. Each function across
all files had to be public. Hopefully it’s just my setup, because this really confused me in the beginning.
Kotlin for scripting
As a fan of Kotlin already, I enjoyed using it for scripting. I could list several reasons why, but I think a few of the key reasons is the type-inference, static typing and lambdas. You can choose between being compact, expressive, or a mixture between the two.
Extracting and rewriting the project
For the most part I had one goal when starting out: The networking-portion was written using ExecutorService and Runnables, I wanted to convert this to coroutines.
The networking portion of the code is a best-case effort due to the underlying implementation of the networking libraries. It works ok enough for a side-project, but don’t use it for anything serious.
The initial version after extraction looked something like this.
After a great deal of rework (mostly due to me reading more up on coroutines, scope and dispatchers) I finished a working proof of concept.
Conclusion
Kscript keeps you in the terminal, and that’s a good thing. When I’m scripting, I want the light experience of editing a file, and running a command in the terminal. Any more overhead, and the barrier is too high compared to other options such as Python or Ruby. With kscript, I can use a text-editor or IDEA, it comes down to personal preference. Execution is fast due to caching. Adding and using third party libraries from the Kotlin and Java ecosystem just works.
I wouldn’t have sought out a way to use Kotlin for scripting unless I liked the language, so this section (and frankly, this entire post) is biased on that part. In terms of scripting I find Kotlin to be concise, flexible, and translates well into scripting-related tasks.
In the future there will probably be even better native scripting support in Kotlin. Until then, I think kscript is a really great alternative. Overall I found using Kotlin for scripting to be a fun and viable solution.