Expressions & Statements

The smallest useful fragment of code in many programming languages is either a statement or an expression.

There’s one simple difference: a statement has an effect, but produces no result. An expression always produces a result.

Because it doesn’t produce a result, a statement must change the state of its surroundings in order to be useful. Another way to say this is “a statement is called for its side effects” (that is, what it does other than producing a result). As a memory aid:

A statement changes state

One definition of “express” is “to force or squeeze out,” as in “to express the juice from an orange.” So

An expression expresses

That is, it produces a result.

The for loop is a statement in Kotlin, so you cannot assign it, since there’s no result:

// ExpressionsStatements/ForIsAStatement.kt fun main(args: Array<String>) { // Can't do this: // val f = for(i in 1..10) {} // Compiler error message: // for is not an expression, and // only expressions are allowed here }

A for loop is called for its side effects.

An expression produces a value, which can be assigned or used as part of another expression, whereas a statement is always a top-level element.

Every function call is an expression. Even if the function has the Unit return type and is called only for its side effects, the result can still be assigned. Unit is a legitimate return value, so a function producing Unit is an expression:

// ExpressionsStatements/UnitReturnType.kt fun unitFun() = Unit fun main(args: Array<String>) { println(unitFun()) val u1: Unit = println(42) println(u1) val u2 = println(0) // Type inference println(u2) } /* Output: kotlin.Unit 42 kotlin.Unit 0 kotlin.Unit */

The Unit type contains a single value called Unit, which you can return directly, as seen in unitFun(). println() also returns Unit. u1 captures the return value of println() and is explicitly declared as Unit while u2 uses type inference.

Because if creates an expression, its result can be assigned:

// ExpressionsStatements/AssigningAnIf.kt fun main(args: Array<String>) { val result1 = if (11 > 42) 9 else 5 val result2 = if (1 < 2) { val a = 11 a + 42 } else 42 val result3 = if ('x' < 'y') println("x < y") else println("x > y") println(result1) println(result2) println(result3) } /* Output: x < y 5 53 kotlin.Unit */

The first output line is x < y, even though result3 isn’t displayed until the end of main(). This happens because evaluating result3 calls println(), and the evaluation happens when result3 is defined.

Notice that a is defined inside the block of code for result2. The result of the last expression becomes the result of the if expression; here, its the sum of 11 and 42. But what about a? Once you leave the block of the code (move outside the curly braces), you can’t access a. It is temporary and is discarded once you exit the scope of the block.

The increment operator i++ is also an expression, even if it looks like a statement. Kotlin follows the approach used by C-like languages and provides two versions of increment and decrement operators with slightly different semantics. The prefix operator is put before the operand, as in ++i, and returns the value after the increment happens. You can read it as “first do the increment, then return the resulting value.” The postfix operator is put after the operand, as in i++, and returns the value of i before the increment occurs. You can read it as “first produce the result, then do the increment.”

// ExpressionsStatements/PostfixVsPrefix.kt fun main(args: Array<String>) { var i = 10 println(i++) println(i) var j = 20 println(++j) println(j) } /* Output: 10 11 21 21 */

The decrement operator similarly has two versions: --i and i--. Using these operators within other expressions is discouraged because it can produce confusing code:

// ExpressionsStatements/ConfusingOperator.kt fun main(args: Array<String>) { var i = 1 println(i++ + ++i) }
Try to guess what the output will be, then check it.

Previous          Next

©2018 Mindview LLC. All Rights Reserved.