Kotlin/Native is a new product from JetBrains. It allows developers to code native applications for Linux, MacOS, Windows, and other platforms using the Kotlin language. Until now, Kotlin has been mostly associated with the JVM and Javascript. But with Kotlin/Native, you’ll be able to write stand-alone apps that run natively. It is currently available in Beta and is, as JetBrains says “feature complete.” This doesn’t mean, however, that all of the features are complete. Instead, it means JetBrains has completed all of the features they intend to complete before initial release. So now is a great time to take a look at it.

Compiling Kotlin Natively

Instead of writing a whole new set of compilers for Kotlin, JetBrains decided to leverage an existing toolchain. So Kotlin/Native uses LLVM. If you aren’t familiar with LLVM, it’s a set of compiler tools first created almost 20 years ago. You may have heard about it as part of Apple’s macOS and iOS development environments. Kotlin/Native compiles Kotlin code down to LLVM’s “Intermediate Representation”, essentially a low-level platform independent language that LLVM understands. From there, LLVM compiles the IR down to the target platform.

Your First Kotlin/Native Project

You may have heard stories about how hard Kotlin/Native is to use. You may have heard that you need to use an unfamiliar IDE and various command line tools. But now you can forget all that. Last year, JetBrains made it’s IntelliJ IDE fully compatible with Kotlin/Native. So now creating a Kotlin/Native project is easy. To prove it, let’s walk through the steps to creating your first Kotlin/Native application.

1) Open the IntelliJ IDE and click on “Create New Project”. For this example, I used a completely standard install of IntelliJ Community Edition.

2) Select “Kotlin” on the left hand panel and “Kotlin/Native” from the right hand panel. Click “Next”.

3) Now just follow the wizard as if you were creating a regular IntelliJ project. Once IntelliJ completes creating your new project, you should have a “Hello World” app that looks something like this:

Congratulations, you are now a Kotlin/Native developer. Easy, right?

Beyond “Hello World”

That’s a nice, painless introduction to your first Kotlin/Native project. But it’s only a “Hello World” console application. Is it actually possible to write more complex native applications using Kotlin? To find out, I decided to port a little 2d space shooter game, written primarily in Kotlin, to Kotlin/Native. I say “primarily” because I used a Java JPanel to paint the graphics and Java KeyListener for accepting user input. But otherwise, the game is written entirely in Kotlin. It’s called Meteoroids and it looks like this:

If Kotlin/Native works as advertised, then I should be able to simply copy and paste the Kotlin code into the Kotlin/Native application with no modifications. To replace the JPanel, I planned to use one of the popular Kotlin UI frameworks. The result looked like this:

As you can see, it’s the same game. The game mechanics worked the same as the original. So as far as the pure Kotlin code is concerned, it really was a copy and paste. But obviously, something about the game is different. It isn’t GUI anymore, right? So what happened? Well, remember when I said that “feature complete” doesn’t mean that all of the features are complete? One of the features that isn’t included in Kotlin/Native is UI support. So I ended up using a character-based user interface instead. That’s a big deal. And I wouldn’t blame you if you stopped reading right now and waited until JetBrains got UI support working before trying Kotlin/Native. But if you’re willing to go beyond your JVM comfort zone. JetBrains provides a workaround which is worth investigating in its own right.

Interoperability With Common C Libraries

JetBrains has made Kotlin/Native interoperable with standard libraries written in C much like JVM Kotlin is interoperable with Java. To demonstrate what this means, I will perform a little magic trick.

So what just happened? I called a function named `getpid` from a standard C library named Posix which returns the process ID. I coded it as if it was Kotlin. And it worked! Take a look at the imports. You will see `platform.posix.getpid`. Posix is a common library that provides many system functions including file I/O. Oh yeah, in addition to UI support, Kotlin/Native also fails to support file I/O. So how does Kotlin/Native know where to get Posix? To find out, look under “External Libraries” in the IDE. I suspect most of us rarely look at this part of the IDE. But now is a good time to investigate it in some depth. You will see a list of native libraries that are supported in Kotlin/Native as if they were Kotlin. And towards the bottom of the list, you can see Posix.

This is a bigger deal than it may, at first, seem. Kotlin/Native gives you access to a rich ecosystem of C libraries. And you can use them as if they were Kotlin. Even if you don’t feel Kotlin/Native is ready for developing production apps. I recommend you try this feature. It is pretty amazing and nearly seamless. One big caveat, however, is that the list of pre-canned libraries varies according to the platform you’re developing on. And Kotlin/Native currently leans very heavily towards the MacOS platform. So the list of libraries available on the Mac is extensive. You will see standard C libraries like Posix as well as platform-specific libraries like the MacOS AppKit. But if you are developing an application for Linux, the list of available pre-canned libraries is surprisingly limited.

I encountered this limitation when I tried to compile my space shooter game for Linux. When I realized that a Kotlin GUI would not be available, I decided to use a UI library called NCurses. It’s a very simple, character-based UI. I found that it has almost no learning curve. So to make it work for my game, I only needed to change some of the sizing parameters (to account for a 100 character wide screen instead of an 1800 pixel wide screen) and replace the png images with ascii images. And that worked great…on my Mac. But when I tried to compile the game for a Linux machine, it failed. The reason is that NCurses is not supported for Kotlin/Native on Linux. The NCurses library is available and widely used on Linux. It just hasn’t yet been implemented on Linux for Kotlin/Native by JetBrains. As you might imagine, this was a huge disappointment. There is, however, a workaround. You can add libraries to your application – even libraries that you write yourself. But be aware that, as of this writing, the official JetBrains documentation for adding libraries is a bit out of date. I’d recommend going to this website : https://learnkotlinnative.com. After many hours of frustration with the official documentation, I followed the author’s instructions and successfully added new libraries to my application.

One More Caveat – Memory Management

I realize memory management is the last thing a JVM programmer wants to think about. But if you use Kotlin/Native, you’ll need to be aware of how you manage your memory. Fortunately, as long as you are writing pure Kotlin code, Kotlin/Native will handle garbage collection for you. But whenever you use a function from an external library, you’re on your own. So how do you manage memory with Kotlin/Native? Fortunately, JetBrains provides a new keyword to help. The keyword is “memScoped”. As the name implies, all of the memory within a defined scope will be handled automatically. You can use memScoped for a block of code or an entire function. You code it in the form ‘memScoped {…}`. Any memory allocated within the curly braces will be automatically freed upon leaving the scope. It’s a pretty painless introduction to memory management for programmers accustomed to automatic garbage collection..

Conclusion

So is Kotlin/Native ready for prime time? The answer is no, it isn’t. Until JetBrains implements things like UI and file I/O, it’s little more than a toy. But if you’re willing to go beyond your JVM comfort zone, you will find it gives you access to a rich ecosystem of libraries that are worth exploring in their own right.