There is no total freedom for spacing in Kotlin

Look at this code in Kotlin Playground

fun main() {
  val eBin = true
  val eHexa = true
  var s = "99"
  println( when { eBin -> "b" 
          eHexa -> "h"  else ->"" } + s)
}

It works nice. However…

fun main() {
  val eBin = true
  val eHexa = true
  var s = "99"
  println( when {eBin -> "b"  eHexa -> "h"  else ->"" } + s)

}

It fails just because when expression is all in one line. There is no ambiguity, because a string cannot appear together with a variable.

If one puts curly braces in the first when option:

fun main() {
  val eBin = true
  val eHexa = true
  var s = "99"
  println(when { eBin -> {"b"}  eHexa -> "h"  else ->"" } + s)
 }

It works again!

Most language that I now don’t consider error any space usage, except inside strings.
I think that Kotlin should follow the same philosophy.

use semicolons

println( when {eBin -> "b";  eHexa -> "h";  else ->"" } + s)
1 Like

Yes, it works, but without semicolons we have a strange combination between compiler errors and no errors.

I thank the OP for pointing me towards a problem I had not considered yet: that semicolons (or other visible separator tokens), annoying as typing and reading them can be, are needed for compilers to parse every weird code formatting corner-case that programmers can think of. And if we do not want semicolons (like in Kotlin, Python, Ruby, Go, Swift and other languages) developers still have to sacrifice something, namely complete freedom of layout. No such thing as a free lunch.

Personally, I like uniform code/a uniform code style, and I am also convinced that uniform code style is the way of the future for programming languages (much better for maintenance, and professional developers tend to do much more maintenance than ‘greenfield coding’). Python’s PEP8, C#/Visual studio’s autostyling, Go’s automatic formatter… Kotlin too (in IntelliJ) has an auto-formatter, which transforms the original line into

println(
    when {
        eBin -> "b"
        eHexa -> "h"
        else -> ""
    } + s
)

No semicolons there, no confusion about how many items one can squash into one line (because, frankly, you shouldn’t if you want to understand the code two months from now, and overall more readable than the original. Though, if I were to need to get this code past code review, I’d probably make it:

val prefix = when {
    eBin -> "b"
    eHexa -> "h"
    else -> ""
}
println(prefix + s)

(and if I were really nitpicky I would point that eBin and eHexa are likely exclusive, so they would be modes in an enum, instead of separate booleans which can give weird results if more than one is set to ‘true’, enum class DisplayMode(val prefix: String) { DECIMAL(""), BINARY(“b”), HEXADECIMAL(“x”) } …val mode = DisplayMode.HEXADECIMAL… println("${mode.prefix}99") which would make the when statement superfluous - but the issue here is the effects of whitespace, not domain-driven design or programming best practices)

So overall, while I agree with the OP that the Kotlin parsing situation is not ‘ideal’, and possibly there could be a request to JetBrains to make their parser yet more intelligent (though I am not sure they can/should do so, as that will decrease compilation speed and thereby negatively affect development, likely more than any added safety or convenience for corner whitespace formatting cases), I consider that as long as people write ‘default formatted code’, the problem is not that handicapping. Compared with the position of Python, for example, where the freedom from semicolons comes with sacrificing easy iterable operation chaining (or at least requires Python developers to insert extra ()), I think Kotlin/the JetBrains team is doing a pretty good job.

2 Likes

Since we’re talking about semicolons… My problem with Kotlin’s semicolon inference is that it’s too eager.

Right now it assumes a semicolon at the end of any line that would compile on its own, even if the following line then gives an error. In my humble opinion, it would be better for it to assume a semicolon only if both the current and following lines would make sense.

I hit this issue very frequently, because I find code much easier to scan if lines wrap before an operator. For example:

override fun toString() = "MyObject(field1=$field1, field2=$field2, field3=$field3, "
                        + "field4=$field4, field5=$field5, field6=$field6)"

This sort of wrapping is fine in Java, but Kotlin wrongly infers a semicolon at the end of the first line, and then gives a syntax error on the second.

Even worse, there are cases where it silently does the wrong thing. For example (within a function):

val a = someLengthyNumericExpression
      - someOtherNumericExpression

Here both lines stand alone. (The second is simply a numeric expression, with a unary minus.) So it compiles, and evaluates the second expression, but does not take it into account when setting the val.

That strikes me as a very dangerous situation!

Why can’t Kotlin infer a semicolon only where both lines would make sense with one? That change wouldn’t break any existing working code, and would prevent annoyances and dangers like the above!

I don’t think there needs to be “total freedom for spacing” in kotlin, as that usually leads to weird unreadable code. This code snippet is a great example of code you should not write.

when is not a one line minded thing. That’s like putting a switch on one line in Java. It makes no sense and it’s annoying to read!

For example, if your when has only 1 branch, you can just use a 1 line if. And if you are using when as an expression, you need 2 conditions (unless you are using a sealed class or enum with only 1 “known value”) and should be using 2 different lines to more easily present that you have 2 different conditions.

So you should definitely be writing it as @EWLameijer stated. Easily understood.

It makes sense that your examples work as they do. There’s nothing “eager” about the semicolons inference as it does exactly what you tell it to according to the language. You can move the operator to the end of the first line and keep the second operand on the second line and it will work as expected.

I find code easier to read the way I stated as well. I don’t get to the end of a line, see nothing and then go to the next line and see the operator. I see the operator at the end of the first line, see the operator and nothing after it and come to the conclusion that there is an operand on the next line.

I don’ t use my example as a “example” of good programming style. It’s just for make a reflection about syntax rules. I don’t like strict rules, I don’t like semi colons and I don’t like spacing syntax limitations…
There is not a only one good style. Some people love spaces and long indents, other people don’t. There is no right answer. But it is just my personal opinion.