Algus

Functions

Kotlin functions are first-class , which means they can be stored in variables and data structures, and can be passed as arguments to and returned from other higher-order functions. You can perform any operations on functions that are possible for other non-function values.

Anatomy of a Function

private fun obtainQuest(
	playerLevel: Int,
	hasAllies: Boolean = false, // default argument
): String {
  // return quest
}

There are four visibility modifiers in Kotlin: privateprotectedinternal, and public. The default visibility is public. [[ Visibility Modifiers ]] ⇡

If a default parameter precedes a parameter with no default value, the default value can only be used by calling the function with named arguments.

fun foo(
    bar: Int = 0,
    baz: Int,
) { /*...*/ }

foo(baz = 1) // The default value bar = 0 is used

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

fun foo(
    bar: Int = 0,
    baz: Int = 1,
    qux: () -> Unit,
) { /*...*/ }

foo(1) { println("hello") }     // Uses the default value baz = 1
foo(qux = { println("hello") }) // Uses both default values bar = 0 and baz = 1
foo { println("hello") }        // Uses both default values bar = 0 and baz = 1

You can pass a variable number of arguments (vararg) with names using the spread operator:

fun foo(vararg strings: String) { /*...*/ }

foo(strings = *arrayOf("a", "b", "c"))

Unit-returning functions

If a function does not return a useful value, its return type is Unit.

fun printHello(name: String): Unit {
	println("Hello $name")
}

💡 The Unit return type declaration is optional.

Single-expression functions

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

fun double(x: Int): Int = x * 2

// Return type is optional as can be inferred
fun triple(x: Int) = x * 3

Infix notation

Functions marked with the infix keyword can also be called using the infix notation (omitting the dot and the parentheses for the call). Infix functions must meet the following requirements:

infix fun Int.shl(x: Int): Int { ... }

// calling the function using the infix notation
1 shl 2

// is the same as
1.shl(2)

The function type

A function type definition consists of:

val msg: () -> String = {  
	"Hello World!"  
}
  
val greet: (String) -> String = { to ->
	"Hello $to!"
}

// Removing optional types
val msg = { "Hello World!"}
val greet = { to: String -> "Hello $to!" }

The Nothing Type

Similar to the Unit type, Nothing indicates that a function returns no value with the exception that this function is guaranteed to never be successfully complete.

/**
* Always throws [NotImplementedError] stating that operation is not implemented.
*/
public inline fun TODO(): Nothing = throw NotImplementedError()

💡 When a function is marked as inline, the compiler will replace every call to that function with the actual code of the function itself, instead of generating a function call instruction at runtime.

One use for it is, as its name indicate, to note that there is still work to do.

fun shouldReturnAString(): String {
	TODO("implement the string building functionality")
}

Backticks function name

This feature is included to support interoperability with other languages and for support expressive names of functions tat are used in a testing file

fun `users should be signed out when they click logout`() {
// Do test
}

// instead of `usersShouldBeSignedOutWhenTheyClickLogout`

[[ Lambda ]] ⇣

In Kotlin, a lambda is an anonymous function that can be treated as a value.

// CharSequence.count(predicate: (Char) -> Boolean)
"A link to the past".count({ letter -> letter == 't' }) // 3

// We can remove the `()` when the last argument is a function
println("A link to the past".count { 
	letter -> letter == 't' 
})

/* 
** When using lambda with a single argument,
** you can either give the argument a name or use the `it` keyword
** as a shorthand way to refer to the argument.
*/
"A link to the past".count { it == 't' } // 3

💡 A return inside a lambda expression will return from the enclosing function.

Syntax

// The value of the last expression is implicitly returned.
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }

// Removing optional annotations out
val sum = { x: Int, y: Int -> x + y }

Anonymous functions

val nums = arrayListOf(1,2,3,4,5)
nums.filter(fun(item) = item > 3)) // [4, 5]

💡 A return statement without a label always returns from the function declared with the fun keyword.

Closures

Kotlin functions can access its closure, which includes the variables declared in the outer scope.

val nums = arrayListOf(1,2,3,4,5)

var sum = 0
nums.filter { it > 0 }.forEach {
    sum += it
}
print(sum) // 15

Variable number of arguments (varargs)

You can mark a parameter of a function (usually the last one) with the vararg modifier:

fun <T> asList(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts) // ts is an Array
        result.add(t)
    return result
}

val ary = arrayOf(1, 2)
// We can pass the contents of an array using
// using the `spread` operator by prefixing the
// array with `*`
val list = asList(0, *a, 3) // [0,1,2,3]

Function Reference

A function reference converts a regular function using the fun keyword into a value with a function type.

// simple lambda sum function
val sum: (Int, Int) -> Int = { x, y -> x + y }

// function that accepts an op lambda
fun applyOp(x: Int, y: Int, op: (Int, Int) -> Int): Int = op(x, y)

// normal usage
applyOp(2, 3, sum)

/*
* Refactor to use function references
*/

// We have to use a function as References to variables aren't supported
fun sum(x: Int, y: Int) = x + y

// Error, as is not detected as lambda
applyOp(2, 3, sum) // Function invocation 'sum(...)' expected

applyOp(2, 3, ::sum) // This works as it uses the reference operator `::`

Extension functions