React’s TicTacToe tutorial, in Kotlin/JavaFX

A simple comparison of web vs desktop productivity

A lot of user interface programmers (“frontend devs”) these days have never worked with anything except HTML and JavaScript. That’s a pity because, as I have previously argued, the web is not a very productive platform.

Recently I read that React was getting really popular as a JS framework so to try and keep my skills at least slightly fresh I figured I’d read the tutorial and get a feel for it. The tutorial shows how to write a tic-tac-toe game. Given that it is a very modern framework, and from Facebook, I was expecting the tutorial to be a walk in the park.

What immediately struck me was how much code is required for such a simple game. At around 240 lines (with HTML, CSS and JS together) it’s a pretty hefty amount of logic for such a small thing. Reading it led me to recall this blog post from 2015 that compared React/Flux to the Windows API. My gut reaction was that a Windows 3.1 version of tic-tac-toe wouldn’t be vastly larger, and surely a version written with a modern GUI toolkit and language would be much shorter. So I decided to do a comparison:

For Windows 3.1, thanks to Rob Fahrni for uploading his version to github. After removing comments and the about box code it weighs in at 598 lines of C.

lines of C. The concatenated code from React’s tutorial, which at 238 LOC is 39% of the size of the Windows code.

is 39% of the size of the Windows code. My version written in Kotlin for the JVM, which is 113 LOC (ignoring import lines), or 47% the size of React’s version and 18% of the size of the Windows version.

This isn’t a very scientific comparison — my code is a straight line equivalent of React’s and as such provides the same UI and features, whereas Rob Fahrni’s code calculates winning differently (more verbosely) and doesn’t have the undo feature that React shoehorns into the game. The original Windows API is a very verbose API that requires large quantities of boilerplate, and C is a very verbose language.

But what’s surprising is how complicated React is. Kotlin with JavaFX requires less than half the amount of code. It is the winner by a mile when comparing by LOC and that’s without any helper frameworks on top that would reduce the line code by a bit more. LOC and productivity aren’t exactly the same, but halving the amount of code you need to read and write is an advantage that can’t be ignored … and that’s before we think about the benefits of static typing for catching errors, and the very clean and light code that results.

Sometimes for distribution or other reasons you just have to suck up the web’s problems. But sometimes you are OK with distributing some other form of program — maybe you’re just writing software for yourself or your company. And then wouldn’t it be nice to get that productivity win?

Without further ado, I present a Kotlin/JavaFX tutorial equivalent to React’s, for JavaScript developers who’d like to get a flavour of what modern desktop development feels like.

The result of our efforts: nothing pretty, but simple

Set up

First, the tools. The easiest way to get what you need and get productive is to install OpenJDK 8 (9 or higher also work), and then install IntelliJ Community Edition — both are free and open source. Then you can use the IDE, which is by far the best way to write Kotlin because the editor understands and can manipulate the code so well. It’s especially useful and important in type safe languages.

But if you’d prefer to use your current text editor and the command line, you can do that too. For example, try sdk install kotlin if you have SDKMAN!, or using brew install kotlin if you're on macOS, or sudo snap install --classic kotlin if you're on Ubuntu. You can then compile a .kt file to a .jar that Java can run using a command line like this:

kotlinc hello.kt -include-runtime -d hello.jar

java -jar hello.jar

NB: If running on more recent versions of Java (post Java 10), use a build system like Gradle and follow the instructions here.

Basic data structures

Let’s start by defining how we’ll represent the two players:

Web apps typically use JavaScript, which is not a type safe language. The React version of TicTacToe therefore uses the strings “X” and “O” to stand in for each player, both in the board and elsewhere. Kotlin has the convenient syntax of a dynamic language but is strongly typed, so it’s more idiomatic to start by defining an enumeration — X and O are in effect singleton objects. Then we create a simple function that returns a label for a Player object.

The label function uses a few convenience features. Kotlin functions that would consist of a single return statement can be written as fun foo() = bar , which saves some keystrokes. A when expression is basically like a switch in JavaScript, and due to the typing IntelliJ can fill out the branches for you. The function is defined as an extension to the Player class - this is different to classical OOP where the method would have been defined inside the enum as a superclass method and then overridden, and is more like JavaScript's prototype based approach. Kotlin supports both of course, but here it's more convenient to use an extension for two reasons:

It’s less verbose to use a when/switch than define a method and override it for each case. We can successfully call this method on a variable of type Player even if the value is null (undefined).

Kotlin keeps track of which variables/parameters can be null and which cannot. Nullable types have a question mark after them.

Now let’s define the data structures.

This code is largely equivalent to the Javascript starting on line 126 of the React solution which looks like this:

However, their code does not define a type to encapsulate the state of a board — they just use an array directly. Here, we hide the array inside a class to make the board state truly immutable. The squares start out as 9 empty squares, which we represent as nulls.

Note also:

coord is another one-line expression function that just converts an (x,y) pair into an index in the flat array.

is another one-line expression function that just converts an (x,y) pair into an index in the flat array. get is a function that reads the array. It's marked as an operator fun . In combination with the special name get this means we can index a board object with square brackets, as if it were a multi-dimensional array.

is a function that reads the array. It's marked as an . In combination with the special name this means we can index a board object with square brackets, as if it were a multi-dimensional array. The with function returns a copy of Board with the given square altered. It uses the also function, which is an extension function defined on Any , the "top type" (i.e. everything is a subclass of Any so we can call also on anything). It lets us run code with the thing you call it on, and then it returns that thing. The code appearing after also is a lambda (anonymous) function with a single parameter. Conventionally in Kotlin, single parameter lambdas don't need to give a name to the parameter, it'll be called it by default. This is yet another feature that lets you write really concise yet readable code. Try reading the function left to right and it nearly sounds like English.

function returns a copy of with the given square altered. It uses the function, which is an extension function defined on , the "top type" (i.e. everything is a subclass of Any so we can call on anything). It lets us run code with the thing you call it on, and then it returns that thing. The code appearing after is a lambda (anonymous) function with a single parameter. Conventionally in Kotlin, single parameter lambdas don't need to give a name to the parameter, it'll be called by default. This is yet another feature that lets you write really concise yet readable code. Try reading the function left to right and it nearly sounds like English. JavaScript supports collection literals with [ and ] but Kotlin doesn’t. So we have to write out explicitly that we want a list of triples.

JavaScript supports destructuring declarations, that bit where lines[i] is split into three variables in one go. Kotlin supports this too, but it also supports a more convenient form of for loop, and you can destructure inside the for loop.

As you can see there are many short forms of things. The IDE has keyboard shortcuts to convert to and from the different forms, so if you write things out the short way then decide it’s more readable the longer way it’s an instant fix.

A quick word about types. val in Kotlin is like const in JavaScript, it defines an immutable name for an expression, or it creates a class property. However, everything in Kotlin is typed. If you don't specify the type explicitly (like this: val foo: String = "bar" ) then the compiler will figure out the type for you.

The IDE can do this too, and you can watch it do this as you type using “inlined hints”. This is text the IDE inserts into the editor buffer which isn’t really there; it won’t appear in the saved file and your cursor skips over it. You may have to enable this under “Editor > General > Appearance > Show parameter name hints > Configure”, but once that’s done you can switch them on or off using (on a Mac) Alt-Enter when the cursor is over a variable definition.

Try adding something to the list that isn’t a triple and you’ll see the inferred type change:

Kotlin type inference in action

Here, we can see that the type of the list has been inferred automatically as List<Triple<Int, Int, Int>> which is quite a mouthful - it's the most precise type Kotlin can figure out. If we start adding things to the list that aren't triples of integers, we can see the inferred type change - first to "a list of things I can serialise" and then "a list that can contain anything". We can choose if we want the editor to provide inline hints, or whether the types should be invisible, or whether we want the type to appear in the saved text. The latter is of course good for people reading the code outside the IDE, where the types of things may not always be immediately apparent.

Reactive utility methods

JavaFX may predate React by some years, but it is nonetheless a fully reactive framework in the Rx/FRP sense. Everything in the API is observable, and you may create your own observable data structures that can be bound bidirectionally to the user interface.

Here I’m defining a couple of useful utility methods to better integrate things with Kotlin. In a real app I’d use a framework like EasyBind or TornadoFX, which adds many such utilities along with higher level constructs. But I don’t need a full blown library for these two simple helpers.

They may look a bit intimidating if you aren’t used to strong typing with generics, but we’ll see an example of their usage in a moment that should make things clearer. For now it is sufficient to say that given an observable list of things and a function, the function will be re-run on the list whenever the list changes, and the result stashed in another observable variable, which is what these functions return. The difference between them is simply what the anonymous function gets passed. So — pretty abstract, but ultimately just a bit of syntax sugar.

The application

JavaFX applications start by sub-classing Application and overriding the start method. Here we define a variable to keep track of the current player, and an observable list of board states. Observable lists are just like regular lists except we can register callbacks on them that get invoked when they change - usefully, the callback can receive a precise 'diff' of what changed too. That's especially good for the animation framework.

We’ll fill out start in a moment. A “Stage” in JavaFX-speak is just a window.

Immutability

We have to keep a list of boards around because the React tutorial tries to demonstrate the benefits of immutability by letting the user go backwards in time to any move in the game, and this tutorial matches React’s. I find this to be rather forced: TicTacToe does not let players rewind at any time, so this is an odd way to promote the use of immutable data structures. And user interfaces are the quintessential place you find mutable state in any application — literally, the user is mutating the state of the app by clicking and typing things. So any good UI framework will not insist on immutability but rather, make it easy to work with state at whatever level is appropriate for your situation. For undo specifically it is often more efficient to keep around deltas rather than simply cloning your entire application state at each point — even with tricks like optimised immutable collections.

Creating the UI

Let’s fill out the start method.

JavaFX thinks of the world as a “scene graph”, which is a set of objects arranged in parent/child relationships that know how to render themselves to the screen. There’s also FXML that lets you construct these scene graphs by loading a file, so that’s like HTML. And finally the scene graph can be styled using a dialect of CSS in which the properties all have vendor prefixes.

So we can see that JavaFX is very analogous to the way HTML is parsed to create a DOM tree that can then be styled with stylesheets and altered at runtime with code.

The nice thing about FXML, and the primary reason to use it, is there’s a tool called Scene Builder that lets you visually create FXML user interfaces.

Scene Builder in action

However, I won’t use FXML or Scene Builder here. Just like with web dev, sometimes declarative UI is convenient and sometimes it’s simpler to just create the elements of your UI programmatically.

override fun start(stage: Stage) {

TODO() val statusLabel = Label()

statusLabel.textProperty().bind(boards.compute { statusText() }) TODO()

}

TODO() is a Kotlin function that just throws an exception. Here we see the JavaFX property pattern at work. Label is a container for text only (there’s no direct equivalent in HTML). We can create a text node like this:

val name = Label()

name.text = "Bob"

which is intuitive enough … but above we use “binding”. This uses the utility method we defined above. It means, connect the text of this label to the output of the statusText function, which should be re-run any time the boards list changes. All writable properties in JavaFX can be bound this way ‘natively’, which is more convenient than anything HTML/DOM offers. Here’s what statusText() looks like:

So the last entry in the boards list is always the current state of the game, and the rest are the history. Same as the React code does it. Adding a new board to the list will cause the status label to change its text.

Layout management

Like all frameworks designed specifically for GUIs, JavaFX handles layout via layout managers and not CSS. HTML is moving towards better layout management too via flexbox, and of course table based design used to be very popular.

You get several kinds of layout manager, all of which can be nested to get a layout that responds naturally to changes in window size. This isn’t quite enough to replace the entire UI depending on size, which would take a bit of extra code, but for mobile you’d want a custom UI designed for this screen size anyway, so this is in practice not a big deal. Here are the most common layout managers:

VBox , which lays out its children vertically. You can configure spacings and expansion modes for each child.

, which lays out its children vertically. You can configure spacings and expansion modes for each child. HBox , which does the same horizontally. You can get a long way with just these two.

, which does the same horizontally. You can get a long way with just these two. GridPane , which is like an html <table> element or spreadsheet - you can arrange children in a grid, and children can span multiple rows and columns.

, which is like an html element or spreadsheet - you can arrange children in a grid, and children can span multiple rows and columns. AnchorPane which keeps the edges of items at their pre-set offsets from the edges of the pane.

which keeps the edges of items at their pre-set offsets from the edges of the pane. BorderPane which lets you put items at each edge and in the center.

which lets you put items at each edge and in the center. StackPane which overlays items on top of each other, with each layer optionally anchored to one of the corners, edges or the center.

which overlays items on top of each other, with each layer optionally anchored to one of the corners, edges or the center. FlowPane in which items flow one after another left to right, top to bottom. You can also do top to bottom, left to right, to get things laid out in columns.

in which items flow one after another left to right, top to bottom. You can also do top to bottom, left to right, to get things laid out in columns. TextFlow which implements something like an HTML <div> ; nodes flow in the order of text direction and text is automatically word-broken. But you can embed any sub-nodes or other layout managers too, e.g. buttons, dropdowns and so on.

which implements something like an HTML ; nodes flow in the order of text direction and text is automatically word-broken. But you can embed any sub-nodes or other layout managers too, e.g. buttons, dropdowns and so on. ScrollPane which is a bit like an iframe, it embeds some other pane that’s larger than itself with optional scrollbars.

CSS can still affect layout somewhat by adjusting spacings, paddings, margins, borders and applying translations. JavaFX uses a dialect of CSS that’s similar to but not identical to web CSS. The things I just named work in the same way though. Nodes are automatically sized by their parents and can be given size constraints for minimums and maximums. In this way the sizes of things are smoothly calculated according to the available size of the screen and window. It takes a bit of getting used to but is very powerful.

We can see immediately that this system makes many tasks that are conventionally hard in HTML trivial in JavaFX. Want a two column layout? Grab an HBox. Want to center something horizontally and vertically? Just stick it inside a StackPane, which does that centering for you automatically.

Now replace the TODO()s in start() with this code:

Hopefully the code makes sense now. We create a vertically stacked set of nodes and apply the CSS class “game-moves” to it. Then we create an anonymous function that will run any time the list of board histories changes (i.e. the player made a move). That function replaces the contents of the gameMoves box with a newly created set of buttons, each one of which will zap the user to that move in the game history. This is similar to how React "renders" changes. Then we create a horizontal box, with the board grid on the left and another vertical stack on the right. Using two vboxes here makes it easy to clear the moves history without accidentally deleting the label too. Finally we add the stylesheet and show the window.

JavaFX CSS

Here’s the CSS file we use. This isn’t web CSS so all the properties have the -fx- vendor prefix, a bit like -webkit-foobar would be. Some of the CSS styles we refer to here like .button come from the default stylesheet, which is a lot more sophisticated than the default HTML stylesheet browsers use. In IntelliJ you can alt-click into these styles to see their definitions in the default 'Modena' skin. Everything here should be self explanatory.

Finishing up

Here’s how we build the game grid.

The only hard bit about this is the bindings again. What we’re saying here is, any time the board history changes i.e. the player made a move, run the given bit of code with it referring to the last state of the board. So the buttons will be disabled if there's a winner, or if that square was filled.

Finally, we need to write the code that handles the button clicks:

We take the last board state in our history, check that the square we’re clicking is really empty (the button disable should ensure this assert never fires), flip the current player and finally, add a copy of the current board state to the history list with the new square filled out. This last line will trigger re-calculation of the button disable states, status label, “Go to move” buttons and so on.

There’s one last bit we need. To run this from within IntelliJ (but not from the command line), we need a main method:

fun main(args: Array<String>) {

Application.launch(TicTacToe::class.java, *args)

}

Putting these last three lines of boilerplate into the editor window will make a green play icon appear in the left hand gutter. Clicking it will run the app. Hint: type “main” and press tab to auto-complete the main method. If you run the app from the command line you don’t need this — the java launcher understands JavaFX Application subclasses and can start them directly.

Conclusion

I’ve written in the past about the productivity problems the web platform has, and proposed some design principles for something that might follow it. But I didn’t try to quantify the benefits before.

Lines of code is not a perfect proxy for “productivity”, which is at any rate a vague term. But it’s not a horrible one either. Writing your code in Kotlin with a real UI framework isn’t just about being more concise or higher level either. It helps in other ways:

Higher performance. JVM bytecode is still faster then JavaScript for many tasks, especially if your problem benefits from shared-memory threading.

Fewer bugs. Even though the Kotlin code has the same sort of visual weight as modern JavaScript, the toolchain (IDE and compiler) is capable of finding many, many common kinds of error as you type.

Advanced automatic refactorings. The shortcuts for intentions and refactorings are your friends if you work in IntelliJ. Selecting a block of code and pressing Cmd-Shift-M to extract a method is something you get used to really fast, and then don’t want to be without. That, and the 100 other similar commands you can use, really make a difference fast.