Kotlin as a better Java and better C yet as demonstrated with a Sudoku verifier

Konrad Banys
Feb 18 · 10 min read
Photo by Marc Reichelt on Unsplash

At Digital Frontiers we all have our favourite languages. The other day we were arguing about advantages and shortcomings of some specific ones and decided to have a closer look at them, using a Sudoku verifier as common example. If you are also interested in other programming languages, here is a collection of links to the other blog posts in our series:

“So why Kotlin?” or boring introduction

I remember when I first had any contact with Kotlin. It was during a presentation at a local programming conference. The lecturer has shown in what an uncompromising way Kotlin removes the boilerplate code and enforces a healthy coding style. It amazed me that someone has gone to such an extent to let a programmer just concentrate on transferring his genius idea to the (obviously black) editor window of the only right IDE.

So why Kotlin again? Because this is the language that has the potential to finally replace Java as the mainstream programming language, greatly facilitating adhesion to healthy coding principles and gently leading the object-oriented world to new concepts. And as my colleagues have already done a great deal to spread the word about the greatness of Kotlin, maybe I could, as well, contribute a little bit to the noble cause.

A couple of features

How to demonstrate the superiority of Kotlin?

Problem: sudoku verifier

Input

private fun readFileContentsIntoList(filePath: String) = File(filePath).loadNumberList(filePath)

“Oh, I did not know the function” — you might say. A quick google search will reveal that there’s no such function. What’s going on then? While this very method is not terribly useful and innovative, it is an example of a very useful construct — an extension function.

fun File.loadNumberList(filePath: String) = readText().trim().split(Regex("\\D+"))

It’s a mechanism that lets you “fix” the “shortsightedness” of the original developers and extend just any class, even the standard ones. Just imagine you could add your own function to String, Integer, or List which makes you feel like a co-author of these classes. Just wow. But jokes aside, you would soon find how handy extension functions are, freeing you from the necessity of writing small but cumbersome utility classes or wrappers.

By the way, with this example, you can already notice how concise Kotlin is, without sacrificing static typing (thus “type inference”). No return type? (But it gets inferred from the return statement) But there’s no return statement?! (return keyword assumed for one-liner function contents). No curly brackets? (Can be omitted for one-liners) And the worst, no semicolon?! (Yes, semicolons are not necessary in Kotlin and that’s one of the features one gets used to pretty immediately).

The simple existence of lists made the process of retrieving numbers from a file and converting them to a two-dimensional sudoku array so much easier (no need for a nodelist class I was forced to use in my C code to accommodate unknown size — either 4x4 or 9x9 — of sudoku in the file) and the concise syntax of array initialization in Kotlin made the code look more elegant, too:

private fun convertElementListToSudokuArray(elements: List<String>): Array<Array<Int>> 
{
val sudokuSize = sqrt(elements.size)
return Array(sudokuSize) { i ->
Array(sudokuSize) { j ->
[...]
elements[i * sudokuSize + j].toInt()
[...]
}
}
}

Verifying sudoku

  • there are only numbers between 1 and Sudoku size (size of the dimension of the sudoku matrix, either 4 or 9)
  • the numbers do not repeat in any row, column or any subsection of the matrix sized sqrt(n) x sqrt(n) where n is the sudoku size.

Kotlin excellent support for lambda expressions for arrays and lists have helped to greatly simplify validation per row:

private fun isRowValid(rowNumber: Int) =

sudoku[rowNumber].groupingBy {
it
}
.eachCount().filter {
it
.value > 1 || it.key !in 1..sudoku.size
}.isEmpty()

column, or subsection:

private fun isSubsectionValid(row: Int, column: Int) =

return sudoku.slice(row until row + sudoku.subSectionSize).map {
it
.slice(column until column + sudoku.subSectionSize)
}.flatMap { it.toList() }
.groupingBy { it }.eachCount().filter {
it
.value > 1 || it.key !in 1..sudoku.size
}.isEmpty()

If you compare the validation code in C and in Kotlin there are several things to notice and to be thankful for. Gone are all the stars and ampersands of the C and C++ pointer world, tedious debugging through memory faults because you treated a value as a pointer and vice versa. Gone are the auxiliary arrays and variables created to facilitate collecting of validating results and gone so much of initialization code — actually all of it!

Notice the very convenient construct of ranges:

row until row + sudoku.subSectionSize

and:

valueToCount.key !in 1..sudoku.size

… that can be used instinctively and understood even by people without coding background. The language designers even thought about a very useful until keyword which is a clean way of showing that the range should finish at the element BEFORE the specified end. The in keyword used with ranges is so much cleaner than “variable is less than…and variable is greater than…” construction.

Grouping of lambda function results made an additional list to detect duplicates redundant and the Kotlin syntax makes everything look clean and easy and triggers you to want to use it extensively to write your statements as effective as possible — just like LINQ in C#:)

And yet another example of how Kotlin makes your life easy — the useful slice method contributes to simplifying the verification routine for a subsection.

Where in the case of C you see a loop after loop with cryptic thread invocations and then again next loops to gather and verify the results of validation, in the Kotlin code there’s basically only one short loop that starts verification of all rows, columns, and subsection and one loop to obtain the final result of validation:

fun isValid(): Boolean {
//starting validation methods
with(sudoku) {
val deferredList = indices.map {
listOf(async { isRowValid(it) },
async { isColumnValid(it) },
async {
isSubsectionValid(
it / subSectionSize * subSectionSize,
it % subSectionSize * subSectionSize
)
})
}.flatMap { it.toList() }
//waiting for the validation to complete, assembling and returning //the final result
return runBlocking {
deferredList.map {
it
.await()
}.none { !it }
}
}
}

You may also notice another Kotlin useful construction: with which is a scope function. In this case, yet again Kotlin delivers on the promise of being concise letting you skip repeating the object name within its context limited by the curly brackets. And if you paid attention, you have noticed the subSectionSize which is an extension property — yes, the extension functions mechanism works also for properties. Placing the calculation of the value in property seems to be inefficient, but in this case, the performance tradeoff is negligible and the change keeps the validation code cleaner.

Coroutines

Run a task asynchronously:

val deferredResult = async { isRowValid(it) }

Verify the task is finished and collect the results:

Code:

val result = deferredResult.await()

The only trace of the fact that something thread-like has been started is the async scope and the await function making sure we’ve collected the results of the coroutine execution. You could say asynchronous execution just cannot get any simpler.

Big advantage of coroutines is that they have a much much smaller footprint comparing to threads. You might consult this short introduction if you are interested in this subject.

DSL

In my case, the configuration is painfully, dreadfully simple. In the end, the only parameter is the file name of the file containing serialized Sudoku. This allows me to create this minute DSL:

sudoku {
file {
<
filePath>
}
}

And then the only thing you have to do is to call the isValid() method on the return value of this construct. Everybody can understand it and the implementation details are completely hidden — only the “domain” is visible.

As one of my colleagues has written an excellent post on Kotlin DSL, I recommend you to consult it if you would like to have a broader picture of the subject and its applications.

Many paradigms

fun sqrt(intValue: Int) = sqrt(intValue.toDouble()).toInt()

I’ve written it because the standard Kotlin sqrt function did not accept integer parameters. But to what class does this function belong? Well, to none, not even to Utils, Tools, or anything of the kind where you used to put your methods which you did not know where to put. It’s a so called top level function that you might know from languages like C or JavaScript. Kotlin developers did not shy away from including the procedural paradigm where it is useful, at the same time gently pushing you, also with the excellent help of the only right coding IDE, towards the functional paradigm. This is where the strength and probably the future success of Kotlin lies.

Wrap up

Nevertheless, I believe that this blog post has shown you how radically Kotlin makes your developer life easier compared with C and in relation to Java, lets you be even more efficient, concentrate on the gist and explore new concepts still within your comfort zone. The carefully designed language constructs make you write code with pleasure and efficiency and make you constantly search whether you can code your idea even more concisely and elegantly. Because with Kotlin you most probably can.

Further reading

Digital Frontiers — Das Blog

Dies ist das Blog der Digital Frontiers GmbH & Co.