A study note based on Kotlin Functions Document.

1 Functions

Funcitons can be defined at top level, local (inside a class/object/function) or as an extension function.

If the last argument after default parameters is a lambda, you can pass it either as a named argument or outside the parentheses.

When a function returns a single expression, the curly braces can be omitted and the body is specified after a = symbol.

A parameter may be marked with vararg. Inside the function the vararg varaible is treated as an array of the specified type. To pass an array as values for an vararg parameter, use the spread operator * to prefix the array.

Use infix to define functions that could be used as infix position.

use tailrec to define tail recursive functions.

2 Function Types

  • Without receiver: (A, B) -> C
  • With receiver: A.(B) -> C
  • Suspending functions: suspend () -> Unit

There are several ways to obtain an instance of a funciton type:

  • a lambda expression: {a, b -> a + b}
  • an anonymous function: fun(s: String): Int { ... }
  • function literal with receiver
  • callable reference
    • a top-level, local, member, or extension function: ::isOdd, String::toInt
    • a top-level, member, or extension property: List<Int>::size
    • a constructor: ::Regex
    • bound callable reference: foo::toString
  • instance of a custom class the implements a function type as an interface

Non-literal values of function types with and without receiver are interchangeable, so that the receiver can stand in for the first parameter, and vice versa. For instance, a value of type (A, B) -> C can be passed or assigned where a A.(B) -> C is expected and the other way around.

Use f.invoke(x) or f(x) to invoke a value of a function type. If the value has a receiver type, the receiver object should be passed as the first argument or use the prepend syntax.

3 Lambda Expressions and Anonymous Functions

The lambda expressions and anonymous functions are function literals, i.e. functions that are not declared, but passed as an expression.

A lambda expression is always surrounded by curly braces, parameter declarations in the full syntactic form go inside curly braces and have optional type annotations, the body goes after an -> sign. If the inferred return type of the lambda is not Unit, the last (or possibly single) expression inside the lambda body is treated as the return value. For example: val sum = { x: Int, y: Int -> x + y }.

Trailing lambda: if the last parameter of a function is a function, then a lambda expression passed as the corresponding argument can be placed outside the parentheses.

It’s very common that a lambda expression has only one parameter. If the compiler can figure the signature out itself, it is allowed not to declare the only parameter and omit ->. The parameter will be implicitly declared under the name it. A return inside a lambda expression will return from the enclosing function.

Use _ for unused variables.

You can not specify the return type for lambda functions, use anonymous function. An anonymous function looks like a regular function declaration except that its name is omitted.

A lambda expression or anonymous function can access its closure.

4 Function Literals with Receiver

Function types with receiver, such as A.(B) -> C can be instantitated with function literals with receiver. To invoke a function type with receiver, you can pass the receiver ojbect as the first argument or prepend it with the receiver object.

Inside the body of the function literal, the receiver object becomes an implicit this and you can access the members of the receiver object without any additional qualifiers: val sum: Int.(Int) -> Int = { other -> plus(other) }.

The anonymous function syntax allows you specify the receiver type of a function literal with receiver, you can use this in funciton body: val sum = fun Int.(other: Int): Int = this + other.

5 Inline Functions

Inline function doesn’t involve memory allocation of classes and objects. Inline lambdas can only be called inside the inline functions or passed as inlinable argument, but not stored in fields and passed around.

Use noinline or crossline to mark functions that are not inline.

Type can be passed as an inline function parameter using the reified type paramter.