An if without an else should return the last line of the body or null

It could be convenient if if would return the contents of the body regardless of whether it is trailed by an else statement.

e.g.

val x = if(someBoolean) "someString"

where x would by of type String?, and have the value "someString" if someBoolean was true, or null if someBoolean was false.

Currently it feels unnecessarily verbose to write

val x = if(someBoolean) "someString" else null

It is not always appropriate to write:

val x = "someString".takeIf { someBoolean }

as there may be some work to perform to create the receiver. e.g.

val x = factory.createObject().takeIf { someBoolean }

and if you want to ā€œoptimiseā€ that it gets difficult to read:

val x = factory.takeIf { someBoolean }?.createObject()
4 Likes

It would probably be incompatible to change the meaning of if in this way.

I would like it to look like this:
if?(someBoolean) "someString"

4 Likes

You can also write your own function that would work like this:

val s: String? = someBool.ifTrue { "someString" }

3 Likes

Great, thatā€™s very close to my original idea, though doesnā€™t quite read as well. i.e. it doesnā€™t read like English. So this proposal could still be a welcome addition to the language.

I donā€™t think so, as if cannot be used as an expression without an else. This means there is no incompatibility as itā€™s not even possible to do. Itā€™s not like it currently returns Unit or null and this would be a breaking change. This would be new functionality and a syntax that currently does not compile.

It just seems logical that if we can write this:

val x = if(someBoolean) a else b

we could write

val x = if(someBoolean) a

it currently gives an error:

ā€˜ifā€™ must have both main and ā€˜elseā€™ branches if used as an expression

This proposal would make the language a little bit more expressive.

2 Likes

This actually would change the behavior of existing code (not sure in which cases it would break anything, though). An implicit else null in a lambda would change the return type of the lambda from Unit to a nullable type.

`val result = someObject.let { if (someCondition) "result" }`

makes result have type Unit while

`val result = someObject.let { if (someCondition) "result" else null }`

makes result have type String?

3 Likes

I think requirement for else clause in this case to prevent developer from missing the false case.

3 Likes

Assiging to result in the above snippet is pointless. There is no reason to assign the value Unit to result in this manner.

If it behaved in the same way as the second snippet then it would be semantically meaningful and terse.

I second this, I think it would be useful. It would of course be trivial to write a custom method ifTrue(condition: Boolean, action: () -> T), as suggested above, but the name ifTrue just looks terrible and confusing, and nothing that Iā€™ve considered looks nearly as elegant as the keyword if.

2 Likes

I created a global extension method for this:

fun <T> Boolean.ifTrue(action: () -> T): T? = if(this) action() else null

This way I can write code like

println("Doing some writing stuff " + CONFIG.dryRun.ifTrue { " (DryRun)" })

or

CONFIG.verificationEnabled.ifTrue { runVerifications() }

or even

errorList.isNotEmpty().ifTrue { transformErrors(errorList) }?.let(::printErrors)
1 Like