Unbracketed blocks in if conditionals


#1

Hi,

I just want to ask about feelings on this. If the following is allowed

if (a)
  b

instead of just

if (a) {
  b
}

it is somewhat easier for a programmer to make the mistake of tacking on an extra statement that one expects to be in the if conditional’s execution block, but is in fact not in it (since there is no block syntactically). I appreciate this removes some of the conciseness Kotlin aims for, but only in a very minor way, and may help prevent bugs. Of course, there is the issue of modifying the parser and backwards compatability…


#2

In Java, I always(*) write

if (a) b;

and haven’t had any single problem with it. Never.

(*) Whenever there’s a single statement which fits the line. I even remove the braces if the number of statements drops to one.

I appreciate this removes some of the conciseness Kotlin aims for, but only in a very minor way.

It’s not minor at all. In this funny piece of a Comparator (real code) it saves half the lines (when compared to else-if; otherwise it’d be two third):

if (a[0] != b[0]) {   if (a[0] == 'B') return -1;    if (b[0] == 'B') return +1;    if (a[0] == 'K') return -1;    if (b[0] == 'K') return +1;    if (a[0] == 'G') return -1;    if (b[0] == 'G') return +1; }

Agreed that

if (a)   b;

wouldn’t help here at all and that it’s prone to the “goto fail” style bug (but automatic indentation should help). But if you want to forbid the latter, you’d probably forbid also the former, and then I’d drop the language. Seriously.


#3

Unbracketed blocks are also much nicer when using if as an expression

val foo = if (bar) 1 else 2

as opposed to

val foo = if (bar) { 1 } else { 2 }

#4

I myself am not sure that I have ever had a problem with unbracketed if expressions causing trouble, but a colleague had an isssue pop up due to this just last week in C++.

I agree unbracketed can look a lot better. But the number of lines of code usually need not increase, e.g.:

if (a[0] != b[0]) {
  if (a[0] == ‘B’)  { return -1  }
  if (b[0] == ‘B’)  { return +1 }
  if (a[0] == ‘K’)  { return -1  }
  if (b[0] == ‘K’)  { return +1 }
  if (a[0] == ‘G’)  { return -1  }
  if (b[0] == ‘G’)  { return +1 }
}

Also, you can have things like this that are unintutive at a glance (imo):

fun myfun(): Int {
var x = 5;
if (something)
  x = x + 1
  return x // This is always executed
return 0 // This is never reached.
}

I don’t care very much either way, just curious how others felt about it - thanks.


#5

I like being able to skip them, because:

if (a)
  b

allows you to fit more code on the screen vertically, which improves reading comphrension. I tend to avoid the   “if (a) b” form these days because then you can’t see if the branch is taken in code coverage tools.


#6

I agree unbracketed can look a lot better. But the number of lines of code usually need not increase, e.g.:
Well, you put braces on the same line. But then, you can n-1 lines in a n line program.

Also, you can have things like this that are unintutive at a glance (imo):

fun myfun(): Int {
var x = 5;
if (something)
  x = x + 1
  return x // This is always executed

I can't. 1. My IDE is set to always fix indentation. 2. I never use the unbraced two-line if.

I  don’t think, a language could profit from forbidding the one-liner and/or the two-liner. That’s something the IDE and style-checkers are for.

Mike Hearn wrote:

I tend to avoid the   "if (a) b" form these days because then you can't see if the branch is taken in code coverage tools.

That’s a good point, but what about a ? b : c,  that’s the same problem, isn’t it?


#7

Yes, and if the branches are non-trivial then I avoid them too. Though usually it's not a big deal because either the branches are trivial (e.g. foo != null ? foo.bar() : null) or you can see if they were taken by looking at the methods they invoke.