25
\$\begingroup\$

Given Google's recent announcement of official Kotlin support for Android development, I thought it might be timely to poll the community for some awesome golfing tips for this relatively new JVM language.

Kotlin includes a unique combination of features among it's JVM siblings which makes it potentially attractive for golfing:

So, how do I squeeze the last few bytes out of my Kotlin program? One tip per answer, please.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ Would there be interest in a golfing language that shortens some of Kotlin's longer names, but doesn't add a lot of extras (at least initially)? I am thinking of making common 1 letter, making string char counts shorter and adding single letter strings with only 1 quote mark? \$\endgroup\$
    – jrtapsell
    Sep 18, 2017 at 18:03
  • \$\begingroup\$ *Common functions \$\endgroup\$
    – jrtapsell
    Sep 18, 2017 at 18:15
  • \$\begingroup\$ Seems Kotlin golfing interest is not that high :( data.stackexchange.com/codegolf/query/793250/top-kotlin-golfers \$\endgroup\$
    – jrtapsell
    Jan 28, 2018 at 22:58
  • \$\begingroup\$ I plan to start submitting more Kotlin solutions! I'll have to check out that project of yours as well. \$\endgroup\$ Jan 30, 2018 at 0:48

11 Answers 11

7
\$\begingroup\$

Use + instead of toString

As one might expect, String overloads the + operator for string concatenation, like so.

print("Hel" + "lo")

However, checking the docs tells us that it accepts Any?, not just String. As stated:

Returns a string obtained by concatenating this string with the string representation of the given other object.

In other words, String + anything makes sure to call .toString() on the right hand side before concatenating. This allows us to shorten it.toString() to ""+it, a massive 8 byte savings at best and 6 bytes at worst.


Use fold instead of joinToString

Related to the above, if you're calling map and then joinToString, you can shorten that by using fold instead.

list.map{it.repeat(3)}.joinToString("")
list.fold(""){a,v->a+v.repeat(3)}
\$\endgroup\$
3
  • \$\begingroup\$ TIL fold is a thing, nice \$\endgroup\$
    – Quinn
    May 13, 2019 at 18:29
  • 1
    \$\begingroup\$ Just as a note to be fair to joinToString, this example shouldn't use map, but instead use the transform lambda of joinToString itself: list.joinToString(""){it.repeat(3)} so the tip only saves 2 characters \$\endgroup\$
    – Joffrey
    Jun 18, 2020 at 0:27
  • 2
    \$\begingroup\$ About ""+it, I find "$it" more convenient because you don't need parentheses when chaining with other stuff \$\endgroup\$
    – Joffrey
    Jun 18, 2020 at 0:36
6
\$\begingroup\$

Starting from 1.3 you can omit args in fun main() completely, thus shaving off 18 chars (that is, the length of args:Array<String>).

\$\endgroup\$
5
\$\begingroup\$

Submitting Lambdas: A Guide

According to meta consensus the default for most challenges allows submitting lambdas/anonymous function bodies. Part of this consensus is that the declaration/assignment of the lambda doesn't have to be included. Take the typical lambda:

val f={a:Int,b:Int->a+b}

We can omit the declaration, that being val f= which saves us 6 bytes.

val f=              // declaration
{a:Int,b:Int->a+b}  // lambda

For the rest of this tip I will write the declaration on one line and the lambda body on the next line to make it clear that it is separate. Know that the declaration line won't count to the score; on TIO you'd include it in the Header and put the lambda as the Code (see here.)

Type Inference and You

Kotlin has a fairly robust system of type inference. If the compiler can infer the type of something, you can omit the type information. Lambdas themselves are typed; their type signatures are expressed as:

(Param1Type, ...ParamNType) -> ReturnType

Therefore, the type of the above is (Int, Int) -> Int.

The type information of a lambda can be inferred from two places:

  • The lambda body
  • The declaration/use site (when assigned to a variable with a type annotation, or passed to a function)

In the given example above, the types of the parameters are stated in the lambda body, and the return type of the lambda can be inferred by the expression in the body because the types of everything is known. This is why the type of the lambda doesn't have to be stated in the declaration; the compiler knows its complete type signature!

...but what if we did?

A Parlor Trick

val f:(Int,Int)->Int=
{a,b->a+b}

By moving the type information of the lambda into the declaration, it's removed from our actual answer. Thanks to inference, the types of a and b are known, and the return type is known, so this compiles, and it's free bytes!

Inferred it Parameter for Single-Param Lambdas

If a lambda only takes one parameter and its type can be completely inferred, you don't even have to specify a parameter in the lambda body! The inferred parameter will be named it. Take the two examples:

val shuf:(List<T>)->List<T>=
{l->l.shuffled()}
                              // -2
val shuf:(List<T>)->List<T>=
{it.shuffled()}

The compiler knows that this lambda takes one parameter and it knows what type it is, so if it isn't bound to a name in the lambda body, it is named it instead. You can use this to save 2 bytes in the degenerate case above, but if you have to refer to the parameter many times, it will be shorter to simply give it a name as in the former.

{it*it*it*it}
{n->n*n*n*n}

Lambdas With Receiver

Kotlin allows you to specify a receiver parameter for a lambda. Basically, you can rebind this inside. You indicate this lambda takes a receiver by writing the receiver type and a dot before the type signature:

Recv.(Param1, ...ParamN) -> Ret

Where this is useful is highly dependent on your approach.

val f:(Int,Int)->Int=
{a,b->a+b}
                       // -1
val f:Int.(Int)->Int=
{this+it}

Of course, what's most useful is that this is the implicit receiver of member access. So if your submission consists mostly of a call chain on one of the parameters, or you refer to a field a couple times, this is extremely useful.

val shuf:(List<T>)->List<T>=
{l->l.shuffled()}
                               // -5
val shuf:List<T>.()->List<T>=
{shuffled()}

It can be a bad call to use this instead of it e.g.

  • If there's only one parameter to the lambda and you don't need to call any methods on it
  • You need to destructure the parameter
  • etc.

Some Examples

Examples can help you learn!

\$\endgroup\$
3
\$\begingroup\$

Extension Functions

Extension functions can really help to reduce the names of built in methods, and chains of them, an example could be:

fun String.c() = this.split("").groupingBy{it}.eachCount()

but this only helps if:

A) The call is long enough to cancel out the definition

B) The call is repeated

Use of lambdas rather than methods

Lambdas can return without using the return keyword, saving bytes

KotlinGolfer

A project I have started here which takes pretty Kotlin code and gives posts with tests and TIO links automatically

\$\endgroup\$
3
\$\begingroup\$

Defining Int's in params

This will likely have some pretty specific use cases where it may be worth it, but in recent question I golfed I found I could save a few bytes by defining my variable as optional parameters rather than defining them in the function.

Example from my answer to this question:

defining variable in the function:

fun String.j()={var b=count{'-'==it}/2;var a=count{'/'==it};listOf(count{'o'==it}-a,a-b,b)}

defining variables as params:

fun String.j(b:Int=count{'-'==it}/2,a:Int=count{'/'==it})=listOf(count{'o'==it}-a,a-b,b)

because var a= is the same length as a:Int= it will be the same number of bytes to define them (this is only the case for Int) however since I now only have 1 line in the function I can drop the {} and I also drop a single ; (the other is replaced with a ,)

So if there are any functions that require defining an Int, and would be a 1 liner if you didn't define the int in the function - then doing it as a parameter will save a couple bytes

\$\endgroup\$
3
\$\begingroup\$

You don't need to add a ; after the imports if the last element is *

for instance, you can do this

import java.util.*import kotlin.random.*import kotlin.random.Random

even this

import java.util.*import kotlin.random.*fun main(){}
\$\endgroup\$
1
  • \$\begingroup\$ Please separate the tips by answers \$\endgroup\$
    – Fmbalbuena
    Jan 31, 2022 at 22:40
2
\$\begingroup\$

The to infix function

There is a standard infix function named to that creates Pairs of two values. It's commonly used with mapOf() for defining Maps but it's can potentially be a lot shorter than the Pair() constructor.

Pair(foo,bar)   //constructor
foo to bar      //best case 
(foo)to(bar)
((foo)to(bar))  //worst case
\$\endgroup\$
1
  • \$\begingroup\$ Most of the time it is better to use ranges instead of pairs (for numbers). foo..bar is shorter then foo to bar also range.last is shorter then pair.second \$\endgroup\$
    – VizGhar
    Apr 11 at 17:35
2
\$\begingroup\$

Destructuring in lambda arguments

Say you want to accept a Pair<*,*> in a lambda. Normally, handling this would be annoying. As an example, here's a lambda that takes a Pair and checks if the two values are equal:

{it.first==it.second}

This is long-winded and clumsy. Luckily, Kotlin allows you to destructure any destructable type (any type that implements componentN() methods, such as Pair, Triple etc.) as arguments to a lambda. So, we can rewrite this in the following way:

{(a,b)->a==b}

It looks similar to pattern matching a tuple in something like F#, and it is in many cases. But a wide variety of types in Kotlin support destructuring (MatchResult is a useful one.)

You can take more arguments, though. Say your lambda had to take a Pair and an additional value. You'd simply write the lambda signature like this:

(a,b),c->  // pair first
a,(b,c)->  // pair second
\$\endgroup\$
2
\$\begingroup\$

you can assign the functions (most important case is the main however) an unit type function

for instance instead of doing fun main(){println("hello world")}

you can do

fun main()=println("hello world")

which shaves off a mere byte

\$\endgroup\$
2
\$\begingroup\$

The repeat function sometimes can shave off a few bytes in some cases

the repeat function basically translates to this,

for repeat(100){something(it)}

it becomes for(it in 0..100-1){something(it)}

the main problem of this is that the it starts from 0 as always and goes until times-1 which might not be ideal for some cases. I'd suggest, for any for loops, converting for loops to repeat loops and checking the byte count, like you might convert it then name the variable i instead of it etc. This will most certainly save you bytes if you use the variable "it" frequent enough.

\$\endgroup\$
0
\$\begingroup\$

Get STDIN

If you want to read a line, you can do readln() instead of readLine()!!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.