Syntactically correct sentences in Kotlin

Rado Yankov
Dev Labs
Published in
8 min readJun 20, 2017

--

Kotlin is great. We’ve all heard of null safety, extension functions and 100% Java interoperability. Short. Fast. Functional. These are all qualities often thrown around in Kotlin articles. But grammatically correct? Syntactically correct? Beautiful? Poetic?Okay, maybe I’m getting a bit too excited, so without further ado, here is how, thanks to some utile trickery, to apply structural, correct (or as correct as it can get) syntax to some basic, everyday operations in Kotlin for Android.

Prerequisites

Before we continue, there are 2 things that I should fill you in on:

  1. Kotlin. A basic understanding of some key language features would suffice, although the more you know, the better.
  2. With this being an Android specific guide, some knowledge of the framework would be more than welcome. Fear not, however, if you are not familiar, for most of the concepts are applicable outside of the Android ecosystem.

With that covered, let us begin with the first in our list.

A side of toast

To warm up, we’re going to make an easy to read toast. The implementation is as follows:

operator fun Context.contains(text: Any): Boolean {
Toast.makeText(this, text.toString(), Toast.LENGTH_SHORT).show()
return false
}

This will allow us to write the most petite looking toast ever:

val Breakfast = "Toasty"
Breakfast in this

The class uses operation overloading, something new (and amazing) in Kotlin, which allows us to overwrite standard operations such as subtraction, addition, division and even crazier things like array brackets and method invocations for any class we wish. In this case, we are overwriting the in operator for the Context class. You might think that overwriting Context’s operators doesn’t sound like the brightest of ideas, given the gravity and extent of the class in the Android framework. Thankfully, there are currently 0 uses for the in operator in the class, so we should be safe. Everything else is pretty simple and standard: “Toast” is a plain String object, and “this” is, of course, the activity’s context.

Note: If you are still worried that something might screw up when overloading an operator of Context, you can take another precautionary measure, and change the parameter that it takes (currentlytext: Any) to text: String so that it only overloads “String in Context” operations. This way you narrow down the possibility of errors.

Second Note: Exercise caution when overloading operators. With great power comes great responsibility and, while overloading String’s “+” might seem like a good idea now, it’s not.

Shared Preferences

I know, I know, by now, the shared preferences libraries are probably in the hundreds, most of them “specifically created and optimized for Kotlin™” . Seeing titles such as “Shared Kreferences” or “sKhared prefs” or any other nonsensical combinations with the letter K is nothing new (and far from original), and for a good reason- it’s easy. I mean, what’s so hard in wrapping up these 2 Java lines:

prefs.getString("key", "");
prefs.edit().putString("key", "value").apply()

into something different? Well, there might be a ton of libraries and code snippets that change the already optimized standard shared prefs API into just a little bit more optimized one, but I’m here to show you how you can do it with style. Take a look:

Locally save 42 into "storage"
val number = Locally load "storage"

Now, isn’t this the classiest piece of code you’ve ever seen?

Well, not only is it good looking, but it actually works. Do you wanna know how it’s done? Introducing infix. One of the most underrated, yet glorious features of Kotlin. Almost any single parameter function can be declared with the ‘infix’ prefix. This will allow you to gracefully skip the pesky ‘.’ preceding the function call and the brackets surrounding the parameter. Here is an example:

infix fun String.equals(par: String) = this.equals(par)

That will allow us to do this:

val isEqual = name equals "Bale"

instead of val isEqual = name.equals("Bale")

Unfortunately, an infix function must be a part of a class, and what’s even worse, it must have one parameter. Hopefully, someday we’ll be able to string together multiple global infix functions with tens of parameters but for now this is all we get.

Combining it with the shared prefs API along with some companion objects (the shiny new name for static functions) and extension functions (I’m not explaining what those are, you should know by now) we can create a class that looks like this:

https://gist.github.com/radoslavyankov/b28c714d421821681582cbb026b2cd08

Somewhat resembling a basic (or “special”, your choice) builder pattern, isn’t it? This class is pretty self explanatory, so after a quick glance, you should be able to figure it all out. There are a couple of functions to note in it:

  1. Save - This one is a companion object function. It’s used for saving a value. As you can see, the only thing it actually does is to simply return an instance of the Locally class. This is so because we want an initialized version of the class for any further operations.
  2. Load - Another static function. Used for loading a value into a variable/constant. Doing significantly more work than it’s buddy save(), when given a string as a parameter, it loads a value from the shared preferences and returns it.
  3. Into - Used in conjunction with save. It’s the function that does the actual saving of the value. Has as a parameter the key of the key-value pair to save.

The load() and save() functions work as initializers for the object and then the into() function is called for the actual operation. An interesting detail is the let method, which saves us some unnecessary strokes.

Note: You might have noticed that the process of loading a value involves a shady looking “sharedPref.all” call, which returns every single preference available. This is done for the sake of simplicity and so that the function can be used in a more brief and concise manner.

A shared preferences instance is contextualized with the initialize() function in the following elegant manner:

Locally initialize this

Where “this” is the context of the app.

Tip: With just a little bit of tweaking to one or two of the functions you can repurpose this class to work as a reader/writer of a text file.

Anyhow, we got shared preferences out of the way. Let’s move on!

For here or to go?

Runnables. Schedulers. Handlers. Timers?…. I’m kidding, don’t use timers. Another day, another easy to use set of APIs. This time, not so many Kotlin wrappers for them. Time to change that!

Do after 2 seconds {
val num = 20
}

Looking good.
This implementation includes our old friends the extension function and some high order functions. Here is the lengthy class that makes it all work:

https://gist.github.com/radoslavyankov/f6f62882f37abf706dcfac9ad57e7b09

We have the standard initialization of a class through the companion object function after(), which also sets the delay of the operation, and then access of the initialized class’ method seconds(). The only somewhat interesting part is the function that gets passed in seconds() (or milliseconds(), if that’s your thing), but that’s about it. That’s … kind of boring, isn’t it? Too many words, if you ask me. As a famous someone once said “Have more than you show, speak less than you know, lend less than you owe”. Well luckily, we don’t owe anyone anything, so let’s just get to the code:

{
val num = 30
num in this
finish()
} after 100.seconds

Delicious. As you might have guessed, after() is an infix function of some class, and 100.seconds is the parameter of after(). “Why the curly brackets around the code, tho?”. The brackets allow us to confine a bunch of operations into a group. So everything you put in between the brackets (no matter what) is going to be turned into a separate function which, hear this, is actually a class (Java is to blame for that). So yes, we can totally transform it, inherit it, pass it, and all of those other nice things you can do with an object. The class that makes this work goes something like this:

infix fun (() -> Any).after(delay: Long) {
Handler().postDelayed({
this()
}, delay)
}

What we’re doing, is making an extension function of () -> Any... Which is a function... An extension function of a function?

Okay, I’m gonna be honest. It’s not actually a function. It just looks that way. As I said earlier, it gets compiled to a class which we can fool around with. So we call our extension function after(), give it “delay” as a parameter, and tell it to invoke this() after “delay” seconds. This trickery will allow us to delay any code for any length of time that we desire, in a pretty and, of course, completely async way.

In case you are wondering how the 100.seconds magic is done, it’s extension properties, and this is the extension:

val Int.seconds: Long get() = this * 1000L

Unfortunately there are no infix extension properties (yet) so the “.” between the integer and the property is gonna have to stay.

That’s all well and good, but what if you don’t want it just once? Well, we can further modify this function and turn it into a repeater:

infix fun (() -> Any).every(delay: Long) {
val handler = Handler()
val runnable = object : Runnable {
override fun run() {
this@every()
handler.postDelayed(this, delay)
}
}
handler.postDelayed(runnable,delay)
}

And use it like so:

{
time += 5
Locally save time into "elapsed"
} every 5.seconds

Note: In some edge cases the grouped up code can interfere with other code nearby and cause some bogus, indecipherable compile errors. An easy fix is to separate our implementation and the problematic code with a good old “;”.

Extras on the side

Some operations didn’t make the list on their own, but I wanted to share them with you nonetheless.

A prettier onClickListener for any view:

infix fun View.clicked(func: () -> Unit){
this.setOnClickListener({
func()
})
}

This, of course, can be applied to functions such as onTouchListener or onFocusedListener or anything else related to views. It’s really easy to use and saves you a couple of characters.

And for our closing act, let’s step away from views and activities and embark on a dangerous journey towards reflection with this infix method which would allow us to invoke a parameterless function of any class:

class Invoke(val name: String) {
infix fun of(cl: Any): Any {
try {
return cl.javaClass.getMethod(name).invoke(cl)
} catch (e: Exception){}
return 0
}

Simply saying Invoke method "toString" of variable would yield us an invocation of the toString() method of the variable object. But beware, because here be dragons… and I also haven't done much testing with it, so you might run into some problems (besides the ones that come standard with reflection).

And that’s it! Those are 5–6 ways to beautify your code and, hopefully, make your code reviewer do a double take and ask himself “How the hell did he do that?”.

Conclusion:

As you saw, new and exciting features such as high order functions, operator overloading, extension functions and infix functions allow us to not only take functional and time saving shortcuts, but can also pave the way to some very good looking, easy to read code. There are many more ways to combine these features, and if you use standard Java techniques like generics and enums you can create even crazier looking implementations.

--

--