Kotlin Scripting in 2024
Written by: Jonathan Augustine
A Little Preamble
Java was the my first real programming language. Learning it was my own cognitive Cambrian Explosion; I was able to create every idea that popped into my head: Discord bots, population genetics emulation, really (really) bad games, et cetera. It was cumbersome at first, but after you’ve used a tool for some time you stop noticing its faults. Accustomed to psvm
and verbose Class definitions, I was satisfied with Java. Until I stumbled across the Kotlin 1.2 announcement and the promise of single-language, multiplatform programming.
Delving deeper into this Kotlin only made my excitement grow. Single-line class definitions? First-class functions? A whole new world of functional programming opened my eyes to just how heavy Java is. I almost immediately started transitioning my existing Java projects into Kotlin, typing away with glee as I watched the line-count shrink. I suddenly had a favorite programming language.
Kotlin’s Entry Cost
Despite Kotlin’s improved readability and transpiling magic, it still suffers from the JVM hero that is Gradle. It’s an amazing tool and has earned it’s place as Kotlin’s primary build tool, but no-one has ever accused Gradle of being easy to get into. In a world of package.json
and cargo.toml
, gradle.build
and gradle.settings
feel like extra roadblocks in the way of actually starting a new project. Just scrolling through r/Kotlin shows how commonly build.gradle.kts
befuddles new programmers. Many tend to brush this off but they forget how long it took them to get accustomed to Gradle’s intricacies. Without IntelliJ’s project creation tools, the tedious boilerplate would certainly cause many newcomers to try another language.
The Potential of Kotlin Scripting
“What if Kotlin could have as little setup as TypeScript” is a thought that’s crossed all our minds at least once. As it turns out, there is (kind-of) a way out of Gradle: scripting! At the time of writing, there are two main solutions for scripting with Kotlin: the independent “KScript” and JetBrains' own Kotlin scripting. In this and following articles we'll explore both solutions to find which one can match the low inertia seen in TypeScript and Python.
The Plan
We’ll start by making a simple REST backend with both scripting implementations; the goal is to see how easy it is to:
1. Get from no project to a running server
2. Create and develop the project without IntelliJ
3. Package and execute the project
A Little Setup
Let’s use VSCode and Ubuntu (WSL2) as a neutral, non-IntelliJ environment. Make sure your installed Kotlin version is >=v1.4
:
$ kotlin -version
Kotlin version 1.9.23-release-779 (JRE 21.0.2)
Installing the Kotlin VSCode extension is a good idea if we still want some code-completion. Note that the extension won’t recognize .kts
files by default for whatever reason, so make sure to change that in settings – even then it is far from perfect.
JetBrains' Kotlin Scripting
The official “experimental” implementation from JetBrains is pretty much plug-and-play, but you might hit an apparent roadblock when exploring the documentation. The official guide for “Kotlin Custom Scripting” (KCS) would lead you to believe there’s much more bloat than necessary. Needing to write a “Script Definition” and “Scripting Host” across multiple files and directories before even touching fun main
is an easy way to turn Kotlin scripting into another Gradle lesson; luckily, KCS isn’t what we’re looking for today. We can easily write Kotlin code with maven dependencies and file imports and barely learn any new syntax!
Let’s make a new directory and our entry file:
$ mkdir kotlin-scripting && touch ./kotlin-scripting/entry.main.kts
Open entry.main.kts
and add a shebang to the top of the file along with a simple print statement:
#!/usr/bin/env kotlin
println("Oh Hi Mark")
This will allow us to run the file without directly using the Kotlin CLI:
$ kotlin ./entry.main.kts
vs
$ ./entry.main.kts
Running the script should print Oh Hi Mark
as expected. Now we’re scripting! (If you get a permission error try $ chmod +x entry.main.kts
).
Now we can add Maven dependencies with just a simple tag:
#!/usr/bin/env kotlin
@file:DependsOn("io.ktor:ktor-server-core-jvm:2.3.9")
@file:DependsOn("io.ktor:ktor-server-netty-jvm:2.3.9")
build.gradle
wishes it was this simple! Let’s flush out the rest of the file with some KTor boilerplate:
#!/usr/bin/env kotlin
@file:DependsOn("io.ktor:ktor-server-core-jvm:2.3.9")
@file:DependsOn("io.ktor:ktor-server-netty-jvm:2.3.9")
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
embeddedServer(Netty, port = 3000) {
routing {
get("/") {
call.respondText("Kotlin Scripting!")
}
}
}.start(wait = true)
Run entry.main.kts
and open 127.0.0.1:3000
and we see “Kotlin Scripting”. In only 19 lines we’ve made a Ktor server! No gradle, no IDEA files, just Kotlin.
Conclusion
Jetbrains’ Kotlin scripting is easy, intuitive, and convenient. Being able to add dependencies without a build file and run .kts
from the command line without downloading anything extra is amazing. “1. Get from no project to a running server:” eazy-peazy. However, it’s not without it’s shortcomings. In the next part we’ll introduce the competing scripting implementation, KScript, and explore some of the general limitations of scripting in Kotlin. See you soon!