1

In Lua (which is rarely used outside of development in Corona SDK), you can evaluate ANY expression in an if statement as follows:

  1. If expression is null, will return false
  2. If expression is the boolean value false, will return false
  3. Everything else will return true

Examples

if (1) // true
if ("Hello World") // true
if (instanceOfSomeRandomClass) // true
if ( [2, null, "foo"] ) // true
if (thisFunctionCallWithReturnNull()) // false
if (0 == 1) // false

if also happens to be an expression in Kotlin, so combining these features, I can see make creative ways to use it.

Is there an equivalent in Kotlin?

I know you can always manually check if (expression != null) and there's nothing wrong with that, but if (expression) is lazier, and I like being lazy :)

El Sushiboi
  • 428
  • 7
  • 19

2 Answers2

2

Citing the Kotlin language specification:

The type of the condition expression must be a subtype of kotlin.Boolean, otherwise it is an error.

So no, there is no direct equivalent in Kotlin.

However, you could potentially emulate the behaviour using a custom extension function on Any?:

fun Any?.toBoolean() = this != null && this != false

See it in action (note the input.toBoolean()):

fun main() {
    evaluate(1)
    evaluate("Hello World")
    evaluate(String())
    evaluate(listOf(2, null, "foo"))
    evaluate(null)
    evaluate(0 == 1)
}

fun evaluate(input: Any?) {
    println("$input is ${input.toBoolean()}")
}

Output:

1 is true
Hello World is true
 is true
[2, null, foo] is true
null is false
false is false

Not sure if this would qualify as "lazier", though.

IlyaMuravjov
  • 2,352
  • 1
  • 9
  • 27
Marvin
  • 13,325
  • 3
  • 51
  • 57
1

To make it more convenient, you can create a higher-order luaIf function:

sealed class LuaIf<T> {
    class True<T>(val value: T) : LuaIf<T>()
    object False : LuaIf<Nothing>()
}

inline fun <T> luaIf(condition: Any?, block: () -> T): LuaIf<T> =
    if (condition == null || condition == false) False as LuaIf<T>
    else True(block())

inline infix fun <T : R, R> LuaIf<T>.els(ifFalse: () -> R): R = when (this) {
    is True -> value
    False -> ifFalse()
}

And use it like this:

luaIf(null) { 1 } els { 0.5 } // 0.5
luaIf(Any()) { 1 } els { 0.5 } // 1

Unfortunately, every time the condition is true (or not null), the LuaIf.True instance is created. You can optimize it by inlining LuaIf class:

inline class LuaIf<T> @Deprecated(
    message = "Not type-safe, use factory method",
    replaceWith = ReplaceWith("luaIf(true) { _value }", "package.file.luaIf")
) constructor(val _value: Any?)

object LuaIfFalse

inline fun <T> luaIf(condition: Any?, ifTrue: () -> T): LuaIf<T> =
    if (condition == null || condition == false) LuaIf(LuaIfFalse)
    else LuaIf(ifTrue())

inline infix fun <T : R, R> LuaIf<T>.els(ifFalse: () -> R): R =
    if (_value == LuaIfFalse) ifFalse()
    else _value as T

Now, if you look at the bytecode of println(luaIf(nullableAny) { 1 } els { 0.5 }), you will see that no additional objects are created. Here is bytecode decompiled by me:

Object $this$els$iv = LuaIf.constructor-impl(
        nullableAny != null && !Intrinsics.areEqual(nullableAny, false) ? 1 : LuaIfFalse.INSTANCE
);
System.out.println(
        Intrinsics.areEqual($this$els$iv, LuaIfFalse.INSTANCE) ? 0.5 : $this$els$iv
);
public final class LuaIf {
   public static Object constructor-impl(@Nullable Object _value) {
      return _value;
   }
}

But since inline classes are experimental, they may lead to some bugs if you use them in a weird way. For example, the following code throws ClassCastException: Integer cannot be cast to LuaIf:

falseLua { luaIf(0) { 0 } } els { 0 }

fun <T> falseLua(other: () -> T): T = luaIf(false) { error("Impossible") } els other

I have already asked a question about this exception.

IlyaMuravjov
  • 2,352
  • 1
  • 9
  • 27