// KOTLINRS — LANGUAGE REFERENCE

kotlinrs v0.1.6 · Kotlin on fusevm · lex/parse → AST → bytecode → Cranelift JIT · no JVM · MIT · in active development

Docs GitHub

>_LANGUAGE REFERENCE

Every keyword, operator, type, builtin call and stdlib member the current kotlinrs build recognizes, grouped into chapters and carrying a signature, a description and a runnable example. This page is generated from the language-server corpus (src/lsp.rs) by the gen-docs binary, so it stays in sync with what the runtime and editor tooling actually know about: keywords mirror lexer.rs and the parser.rs modifier table, operators mirror lexer::operator, types mirror ast::Type::from_name, builtins mirror the arms of compiler::compile_call, and each member mirrors an arm of host::kt_method, char_method, obj_method, sequence_member or coll_hof. Descriptions state what this runtime does; where the behaviour diverges from Kotlin proper, the entry says so.

Keywords & Declarations

# fun

fun name(p: T, …): R { … }
fun name(p: T, …): R = expr

Declares a function. Execution enters fun main, with or without an args: Array<String> parameter. A = expr body is a single-expression function. Only fun, class, object and interface may appear at the top level — kotlinrs has no top-level val/var.

fun cube(n: Int): Int = n * n * n
fun main() { println(cube(3)) }   // 27

# val

val name: T = expr

Declares a read-only binding. The type annotation is optional and inferred from the initializer when it is absent.

val x = 41
println(x + 1)   // 42

# var

var name: T = expr

Declares a reassignable binding. The compound assignments (+=, -=, *=, /=, %=) and ++/-- all write through a var.

var i = 0
i += 1
println(i)   // 1

# if

if (cond) { … } else { … }

Conditional branch. It is also an expression whose value is the last statement of the branch taken; an if with no else that falls through evaluates to null.

val m = if (3 > 2) 3 else 2
println(m)   // 3

# else

if (cond) { … } else { … }
when { … else -> … }

The fallback branch of an if, and the catch-all arm of a when. In a when it is terminal: arms written after it are unreachable and are not compiled at all.

println(if (4 % 2 == 0) "even" else "odd")   // even

# while

while (cond) { … }

Loops while the condition stays true. kotlinrs has no do … while; the post-test loop is not part of the grammar.

var i = 0
while (i < 3) i += 1
println(i)   // 3

# for

for (v in a..b) { … }
for (v in iterable) { … }

Iterates a range or an iterable value — a List, Set, Array, range, or String (by UTF-16 code unit). A syntactic range header compiles to a counted loop the JIT can trace; every other receiver goes through the host iterator. A Map is not iterable here and faults at run time.

for (i in 1..3) print(i)      // 123
for (c in "ab") print(c)      // ab

# in

for (v in iterable)
value in container

Two roles: the for header separator, and the membership operator over a range, List, Set, Array, a Map's keys, or a String's substrings. A when arm may also read in a..b.

println(2 in 1..3)                  // true
println("b" in "abc")               // true
println("a" in mapOf("a" to 1))     // true

# return

return
return expr

Returns from the enclosing function. A bare return yields Unit. There is no labeled return@label for returning out of a lambda.

fun answer(): Int { return 42 }
fun main() { println(answer()) }

# until

a until b

Half-open ascending range: a until b includes a and excludes b. An infix function, so it binds looser than .. and than arithmetic.

for (i in 0 until 3) print(i)   // 012

# downTo

a downTo b

Descending inclusive range, stepping by -1. Printing one shows the progression form, a downTo b step 1.

for (i in 3 downTo 1) print(i)   // 321

# step

range step n

Re-steps a range into a progression. Its left operand is any range expression, not just a literal one, because step is an infix function at a looser precedence than ... A non-positive n raises IllegalArgumentException.

for (i in 0..10 step 5) print(i)   // 0510
println(1..10 step 3)              // 1..10 step 3

# when

when (subject) { cond -> … else -> … }
when { boolExpr -> … }

Multi-way branch, statement or expression. The subject form tests each arm against the subject with ==, in, or is; the subjectless form tests each arm as a Boolean. Arms are tried top to bottom and the first match wins; several conditions may share one arm, comma-separated. With no matching arm and no else the value is null.

val n = 5
println(when (n) { 1 -> "one"; in 2..9 -> "few"; else -> "many" })   // few

# is

value is Type
value !is Type

Runtime type check, in a when arm or in ordinary expression position. It compares the receiver's runtime class tag, so it recognizes the built-in kinds (Int, String, List, …) and user classes including inherited supertypes. A trailing ? on the type and a type-argument list are both accepted and ignored.

val x: Any = "s"
println(when (x) { is String -> "str"; is Int -> "int"; else -> "?" })   // str

# class

class Name(val p: T, …) : Super(args), Iface { … }

Declares a class. Only the primary constructor exists — there are no secondary constructors and no init block. A val/var primary-constructor parameter becomes a stored property; a plain parameter does not. The supertype list may name one class and any number of interfaces.

open class Animal(val name: String)
class Dog(n: String) : Animal(n)
fun main() { println(Dog("rex").name) }   // rex

# interface

interface Name { fun m(): T; fun d(): T = expr }

Declares an interface. A member with no body is abstract; one with a body is a default implementation an implementor inherits. Interfaces cannot be instantiated. A soft keyword — interface is still usable as an ordinary identifier elsewhere.

interface Greeter {
    fun greet(): String
    fun loud(): String = greet().uppercase()
}
class En : Greeter { override fun greet(): String = "hi" }
fun main() { println(En().loud()) }   // HI

# object

object Name { … }

Declares a singleton, constructed once before main runs and reachable by its own name. kotlinrs has no companion object and no anonymous object : T { } expression.

object Counter { var n = 0 }
fun main() { Counter.n = 5; println(Counter.n) }   // 5

# data

data class Name(val p: T, …)

Marks a class as a data class, which generates equals, hashCode, toString, copy and componentN over the primary-constructor properties only. An inherited field is carried but is not part of the comparison and is skipped by componentN. A soft keyword — x.data is still a legal property read.

data class Pt(val x: Int, val y: Int)
fun main() { println(Pt(1, 2)) }   // Pt(x=1, y=2)

# open

open class Name
open fun m(): T

Marks a class as extendable or a member as overridable, and it is enforced. Inheriting from a class that is not open is rejected with B is final, so it cannot be inherited from, and overriding a member that is not open with f in B is final and cannot be overridden .

open class Base { open fun f(): Int = 1 }
class D : Base() { override fun f(): Int = 2 }
fun main() { println(D().f()) }   // 2

# override

override fun m(): T { … }

Replaces a supertype's member, and it is required — redeclaring a supertype member without it is rejected with f hides a member of supertype B and needs an override modifier , and writing it on a member that overrides nothing is rejected too. Dispatch is by the receiver's runtime class, resolved at the call site against every instantiable implementor; a single candidate compiles to a direct call with no test.

open class Base { open fun f(): Int = 1 }
class D : Base() { override fun f(): Int = 2 }
fun main() { val b: Base = D(); println(b.f()) }   // 2

# abstract

abstract class Name
abstract fun m(): T

Declares a class that cannot be constructed, or a member with no body that subtypes must supply. Constructing one is a compile error: cannot construct abstract class Name.

abstract class Shape { abstract fun area(): Double }
class Sq(val s: Double) : Shape() { override fun area(): Double = s * s }
fun main() { println(Sq(3.0).area()) }   // 9.0

# sealed

sealed class Name

Declares a sealed class — abstract, and conventionally the root of a closed set of subtypes matched by is arms. kotlinrs treats it exactly as abstract: it does not check that a when over the subtypes is exhaustive, so an unmatched subject still falls through to null.

sealed class Expr
data class Num(val v: Int) : Expr()
fun main() { val e: Expr = Num(3); println(when (e) { is Num -> e.v; else -> 0 }) }   // 3

# final

final fun m(): T

Accepted and discarded — it restates the default. A class or member that is not marked open is already final, and the compiler enforces that, so writing final changes nothing.

open class B { open fun f(): Int = 1 }
class C : B() { final override fun f(): Int = 2 }
fun main() { println(C().f()) }   // 2

# private

private fun m()
private class C

A visibility modifier. public, private, internal and protected are all parsed and then discarded — a single-file program has no visibility boundary to enforce, so every declaration is reachable from every other one.

private class Box(val v: Int)
fun main() { println(Box(7).v) }   // 7

# inner

inner class C

Accepted and discarded. kotlinrs has no nested classes at all — every class is top-level — so the modifier never changes what is compiled.

class Holder(val v: Int)
fun main() { println(Holder(1).v) }   // 1

# super

super.m(args)
super<T>.m(args)

Calls the supertype's implementation rather than the overriding one, resolved statically. The unqualified form walks the linearized ancestry for the nearest supertype that implements the member; super<T> names the supertype explicitly, and T must be a direct one.

open class B { open fun f(): Int = 1 }
class D : B() { override fun f(): Int = super.f() + 1 }
fun main() { println(D().f()) }   // 2

# this

this
this.property

The receiver inside a class method or a property initializer. A bare name that matches a property of the enclosing class resolves to this.name implicitly, so writing this is optional. There is no this@Label.

class P(val x: Int) { fun twice(): Int = this.x * 2 }
fun main() { println(P(4).twice()) }   // 8

# it

{ it }

The implicit single parameter of a lambda written with no parameter list. A lambda with an explicit list ({ a, b -> … }) has no it. Its type is unknown to the compiler, so it + 1 lowers to a native op that stays on the fast path when the value turns out to be a number and is handed back to the runtime when it turns out to be a Char — which is what keeps { it + 1 } over a List<Char> correct.

println(listOf(1, 2, 3).map { it * 2 })   // [2, 4, 6]

# break

break
break@label

Exits the enclosing loop. break@label exits the loop carrying that label, which is written label@ before the for/while.

for (i in 1..9) { if (i == 3) break; print(i) }   // 12

# continue

continue
continue@label

Skips to the enclosing loop's next iteration. continue@label targets a labeled loop, which is how an inner loop advances an outer one.

outer@ for (i in 1..2) { for (j in 1..3) { if (j == 2) continue@outer; print("$i$j") } }   // 1121

# try

try { … } catch (e: T) { … } finally { … }

Guarded block, and an expression: its value is the last statement of the body, or of the handler that ran. While an exception is unwinding, println/print are suppressed so nothing is emitted between the throw and its handler.

val n = try { 1 / 0 } catch (e: ArithmeticException) { -1 }
println(n)   // -1

# catch

catch (name: Type) { … }

A handler arm. The first arm whose type the in-flight throwable is an instance of wins, walking the modelled JVM parent chain — so catch (e: Exception) catches an IllegalArgumentException. catch (e: Throwable) catches anything.

try { "x".substring(9) } catch (e: Exception) { println(e.message) }

# finally

finally { … }

Cleanup block, run on the normal and the exceptional path alike. Its own value is discarded — the try expression's value still comes from the body or the handler.

try { println("work") } finally { println("done") }

# throw

throw expr

Raises a throwable. Kotlin types it Nothing, so it is an expression and may appear on the right of ?: or as the body of a when arm. An uncaught throw prints Exception in thread "main" <fqn>: <message> and exits non-zero.

val x: Int? = null
val v = x ?: throw IllegalStateException("missing")
// Exception in thread "main" java.lang.IllegalStateException: missing

# null

null

The null reference, carried internally as the VM's undefined value. It prints and interpolates as null. Nullability is not checked statically: T? is parsed and discarded, so a null only makes itself known through ?., ?:, !!, or a runtime fault.

val x: Int? = null
println(x ?: 0)   // 0

# true

true

The Boolean true literal. && and || short-circuit on it; a lambda predicate is only treated as satisfied when it returns exactly true (a null or non-Boolean result counts as false).

val ok: Boolean = true
println(ok)   // true

# false

false

The Boolean false literal.

val done: Boolean = false
println(!done)   // true

# import

import a.b.c
import a.b.*
import a.b.c as alias

Records an import. It matters for exactly one thing: kotlin.math names are unresolvable without it, matching Kotlin. A star import opens the whole package; a single-name import opens only that name; an as alias *replaces* the original spelling, so after import kotlin.math.abs as absolute the name abs is no longer in scope.

import kotlin.math.*
fun main() { println(sqrt(9.0)) }   // 3.0

# package

package a.b

Accepted and discarded. A kotlinrs program is a single file with no package-level name resolution, so the declaration exists only so real Kotlin source parses.

package demo
fun main() { println("ok") }

# as

import a.b.c as alias

The import-renaming keyword — and only that. kotlinrs has no as cast operator and no as?: x as Int is a parse error. Use is for a runtime type check.

import kotlin.math.abs as absolute
fun main() { println(absolute(-4)) }   // 4

# rust

rust { … }

An inline Rust FFI block, recognized before lexing and rewritten in place into a __rust_compile("<base64>", line) call. The block must sit inside a function body. Its #[no_mangle] pub extern exports become callable barewords from Kotlin, dispatched by name at run time.

fun main() {
    rust { #[no_mangle] pub extern "C" fn twice(n: i64) -> i64 { n * 2 } }
    println(twice(21))   // 42
}

# companion

class C { companion object { val K = 7; fun of(…): C = … } }

Declares the class's singleton companion. Its properties and functions are reached through the class name (C.K, C.of(…)) and, from inside the class, without any qualifier. One per class; a named companion is reached the same way.

class C { companion object { val K = 7 } }
println(C.K)   // 7

# vararg

fun f(vararg xs: T)

Collects the call's trailing positional arguments into an array of the declared element type, which the body iterates or measures like any array. Supported as the last parameter.

fun total(vararg xs: Int): Int { var t = 0; for (x in xs) t += x; return t }
println(total(1, 2, 3))   // 6

# by

val name: T by lazy { … }

Property delegation, on a top-level property, a class property or a local val. Only by lazy is supported: the block runs at the FIRST read and its value is cached, so an initializer with an effect fires at use rather than at startup. lazy requires val; any other delegate is a compile error.

val z: Int by lazy { println("forcing"); 42 }
fun main() { println("before"); println(z); println(z) }

Operators

# +

a + b

Addition on numbers, concatenation on String, and code-unit displacement on Char ('A' + 1 is 'B'). Two Int operands wrap on overflow; a Double operand makes the result Double. Its method spelling is plus.

println(1 + 2)          // 3
println("a" + "b")      // ab
println('A' + 1)        // B

# -

a - b
-a

Subtraction, and unary negation. Char - Char is the Int distance between the code units; Char - Int is a Char. Its method spelling is minus.

println(5 - 2)          // 3
println('c' - 'a')      // 2

# *

a * b

Multiplication, wrapping for two Int operands. Its method spelling is times. There is no String * Int repeat operator — use String.repeat.

println(6 * 7)   // 42

# /

a / b

Division. Two integral operands divide truncating toward zero; a Double operand switches the whole expression to IEEE-754 division. Integer division by zero raises ArithmeticException: / by zero; Double division by zero yields Infinity.

println(7 / 2)       // 3
println(7 / 2.0)     // 3.5

# %

a % b

Remainder, taking the dividend's sign for integers (-7 % 2 is -1). Integer % by zero raises ArithmeticException. Its method spelling is rem.

println(7 % 3)    // 1
println(-7 % 3)   // -1

# =

name = value
recv.prop = value
recv[i] = value

Assignment to a var, to a property, or to an indexed slot. An indexed write into a Map inserts the key when it is absent; into a List or Array it is bounds-checked and raises IndexOutOfBoundsException past the end.

var i = 1
i = 2
val m = mutableMapOf<String, Int>()
m["a"] = 1
println("$i ${m["a"]}")   // 2 1

# +=

target += value

Compound add-assign. It works on a var, a property, and an indexed slot alike, and reuses the + semantics — so it concatenates on a String target.

var s = "a"
s += "b"
println(s)   // ab

# -=

target -= value

Compound subtract-assign, on a var, a property, or an indexed slot.

var n = 5
n -= 2
println(n)   // 3

# *=

target *= value

Compound multiply-assign.

var n = 6
n *= 7
println(n)   // 42

# /=

target /= value

Compound divide-assign, truncating when both sides are integral.

var n = 9
n /= 2
println(n)   // 4

# %=

target %= value

Compound remainder-assign.

var n = 9
n %= 4
println(n)   // 1

# ++

x++
++x

Increment by one. The postfix form's value is the target *before* the update, the prefix form's is the value *after* — and both work in expression position, not only as a statement. The target may be a variable, a property, or an indexed element.

var k = 0
println(k++)   // 0
println(++k)   // 2

# --

x--
--x

Decrement by one, with the same prefix/postfix value rule as ++.

var k = 2
println(k--)   // 2
println(k)     // 1

# ==

a == b

Structural equality. It compares numbers by value across Int/Double, Lists element-wise, Sets order-insensitively, Maps entry-wise, and data-class instances over their primary-constructor properties. An Array inherits identity equality, so arrayOf(1) == arrayOf(1) is false. Contrast ===.

println(listOf(1, 2) == listOf(1, 2))   // true
println(setOf(1, 2) == setOf(2, 1))     // true

# !=

a != b

The negation of ==, with the same structural rules.

println(1 != 2)   // true

# ===

a === b

Referential identity: whether both sides denote the SAME object, which == does not ask. Two independently built collections are == and not ===, and a data class's generated equals changes only the former. Values this runtime does not box — numbers, Char, Boolean, String, null — compare by value, so 1 === 1 and "x" === "x" are true as they are on the JVM's interned literals. Two boxing artifacts are NOT modelled: an Any-typed integer outside the JVM's Integer cache, and a String assembled at run time, both of which the reference toolchain answers false.

println(listOf(1) === listOf(1))   // false
println(listOf(1) == listOf(1))    // true
val a = mutableListOf(1)
println(a === a)                   // true

# !==

a !== b

The negation of === — true when the two sides are different objects.

println(listOf(1) !== listOf(1))   // true

# <

a < b

Less-than. Numbers compare numerically and Chars by code unit. Strings do not compare with the relational operators here — kotlinrs implements no compareTo on String.

println(1 < 2)       // true
println('a' < 'z')   // true

# >

a > b

Greater-than, over numbers and Chars.

println(3 > 2)   // true

# <=

a <= b

Less-than-or-equal, over numbers and Chars.

println(2 <= 2)   // true

# >=

a >= b

Greater-than-or-equal, over numbers and Chars.

println(2 >= 3)   // false

# &&

a && b

Short-circuiting logical AND: the right operand is not evaluated when the left is false.

val xs = listOf(1)
println(xs.isNotEmpty() && xs[0] == 1)   // true

# ||

a || b

Short-circuiting logical OR: the right operand is not evaluated when the left is true.

println(false || 1 < 2)   // true

# !

!a

Logical negation. Written twice as a postfix it is instead the not-null assertion !!, and followed by =, in or is it forms !=, !in, !is.

println(!(1 > 2))   // true

# ..

a..b

Inclusive ascending range. It is a value, not only a loop header: it can be bound, passed, iterated, and asked for sum, first, last, reversed and the higher-order collection functions. 'a'..'z' builds a Char range.

val r = 1..5
println(r.sum())   // 15

# ?.

recv?.member
recv?.method(args)

Safe call. The receiver is evaluated once; when it is null the whole expression is null and the member is never dispatched. Chains short-circuit as a unit.

val s: String? = null
println(s?.length)   // null

# ?:

a ?: b

Elvis. Yields the left operand unless it is null, in which case it evaluates and yields the right. Right-associative, binding looser than arithmetic and tighter than the comparisons — and its right side may be a throw.

val x: Int? = null
println(x ?: 0)   // 0

# ::

::fn
Type::member
receiver::member

Callable reference: names a function as a VALUE. With no receiver it is a top-level function or a primary constructor; with a TYPE receiver it is unbound, so the resulting function takes the receiver as its first parameter; with any other expression it is bound, and that receiver is evaluated once where the reference is written. Type::class is not supported.

fun inc(x: Int) = x + 1
println(listOf(1, 2).map(::inc))            // [2, 3]
println(listOf("aa", "b").map(String::length))   // [2, 1]

# !!

expr!!

Not-null assertion: yields the operand, or raises NullPointerException when it is null. Lexed as two consecutive ! tokens, so a != b is unaffected.

val x: Int? = 1
println(x!! + 1)   // 2

# ?

T?

Marks a type nullable. kotlinrs parses the mark and then discards it — there is no static null checking, so a nullable and a non-nullable annotation compile identically and a null only surfaces at run time.

val x: Int? = null
println(x)   // null

# []

recv[index]

Indexed read. On a String the index is a UTF-16 code unit offset and the result is a Char; on a List, Set or Array it is a bounds-checked position; on a Map it is a key lookup that yields null when absent. Chainable: m[k][i].

println("abc"[1])                 // b
println(listOf(10, 20)[1])        // 20
println(mapOf("a" to 1)["z"])     // null

# []=

recv[index] = value

Indexed write. A List or Array slot is bounds-checked and raises IndexOutOfBoundsException past the end; a Map key is inserted when absent. Compound forms (xs[0] += 1) go through the same path.

val xs = mutableListOf(1, 2)
xs[0] = 9
println(xs)   // [9, 2]

# ->

{ params -> body }
cond -> result

Two roles: it separates a lambda's parameters from its body, and a when arm's conditions from its result. It also appears in a function type annotation, (Int) -> Int, whose parameter and return types are parsed and discarded.

val f: (Int) -> Int = { n -> n * 2 }
println(f(21))   // 42

# @

label@ for (…) { … }
break@label

Loop labels. A label@ prefix names the loop that follows, and break@label / continue@label target it. There is no this@Label and no return@label.

outer@ for (i in 1..3) { for (j in 1..3) { if (j == 2) continue@outer; print(i) } }   // 123

# $

"text $name text ${expr}"

String template interpolation. A bare $name splices an identifier; ${…} splices an arbitrary expression, re-parsed from the source between the braces. Each interpolated value is rendered by the same stringifier println uses, so a Double keeps its .0 and null reads as null. \$ escapes a literal dollar.

val x = 2
println("x=$x sq=${x * x}")   // x=2 sq=4

# to

first to second

The infix Pair constructor — the only way to build a Pair here, since Pair(a, b) is not a resolvable constructor. It is what mapOf takes as each argument.

val p = 1 to "one"
println(p.first)   // 1

# !in

value !in container

Negated membership, over the same containers in accepts. A when arm may also read !in a..b.

println(4 !in 1..3)   // true

# !is

value !is Type

Negated runtime type check, usable in a when arm and in ordinary expression position.

val x: Any = 1
println(x !is String)   // true

# .

recv.member
recv.method(args)

Member access. kotlinrs does not distinguish a property from a zero-argument method: both resolve through the same dispatch, so xs.size and xs.size() — and xs.sum and xs.sum() — are the same call. Chains are left-associative and bind tighter than the prefix unary operators.

println(listOf(1, 2, 3).size)   // 3

# :

name: Type
class C : Super(), Iface

Two roles: the type annotation separator on a binding, parameter or return type, and the supertype-list introducer on a class. Any identifier is accepted as a type name; only the eight primitives resolve to a static type, and every other name is carried as an opaque class name for dispatch.

val n: Int = 1
println(n)   // 1

# ;

stmt; stmt

The optional statement separator. Newlines already terminate statements, so ; matters only when two statements share a line — including inside a when arm list written on one line.

var a = 1; a += 1; println(a)   // 2

# as

value as T
value as? T

A checked cast. The runtime value is unchanged — what the cast supplies is the static type T, which then decides integer width and / dispatch downstream. A mismatch throws ClassCastException; the safe form as? yields null instead. Int and Long share one runtime representation here, so a cast cannot tell them apart.

val a: Any = 5
println((a as Int) / 2)   // 2
println(a as? String)     // null

Types

# Int

Int

Signed 32-bit integer. Values are carried in a 64-bit slot at run time, so every Int arithmetic result is narrowed back to 32 bits at the point it is produced: Int.MAX_VALUE + 1 wraps to Int.MIN_VALUE, exactly as on the JVM. / and % truncate toward zero.

val n: Int = 7 / 2
println(n)              // 3
println(Int.MAX_VALUE + 1)   // -2147483648

# Long

Long

Signed 64-bit integer, sharing Int's division rules. An L literal suffix marks a value as 64-bit and prints without it, so println(10L) writes 10. A Long operand keeps the whole expression at 64 bits, so it is not narrowed the way an Int result is: 2147483647L + 1L is 2147483648.

val big: Long = 10L
println(big)                 // 10
println(2147483647L + 1L)    // 2147483648

# Double

Double

IEEE-754 double. It stringifies the way the JVM does: a whole value keeps a trailing .0, magnitudes outside [1e-3, 1e7) switch to scientific form (2.5E7), and the non-finite values print as NaN / Infinity / -Infinity.

println(3.0)          // 3.0
println(1.0 / 0.0)    // Infinity

# Float

Float

Accepted as an annotation and as an f-suffixed literal, but there is no distinct single-precision type: Type::from_name folds Float into Double, so a Float is stored, computed and printed at double precision.

val f: Float = 1.5f
println(f)   // 1.5

# Boolean

Boolean

The true/false type, printed as true or false. A lambda predicate must return one: any other result — including null — is treated as not satisfied.

val b: Boolean = 1 < 2
println(b)   // true

# Char

Char

A single UTF-16 code unit, and a distinct type from Int — it is carried as a tagged handle rather than a number, which is what makes it print as its character inside a List or a Map. Supports +/- displacement, comparison, .code, and 'a'..'z' ranges.

val c: Char = 'A'
println(c + 1)         // B
println(listOf(c))     // [A]

# String

String

Text, with + concatenation and "$x" interpolation. Every length, index and slice position is a UTF-16 code-unit offset, matching the JVM contract rather than the Unicode scalar count. Indexing yields a Char; iterating a String walks its code units. String *literals* are read one source byte at a time, so a non-ASCII character written inside "…" splits into its UTF-8 bytes — a Char literal ('é') decodes correctly, a string literal does not.

val s: String = "n = ${1 + 1}"
println(s)   // n = 2

# Unit

Unit

The no-value type — the result of a function with no return and of println, forEach and the other effect-only calls. The compiler renders it statically as the literal kotlin.Unit.

fun log(): Unit { println("hi") }
fun main() { log() }

# Any

Any

The top type. It resolves to no static type here, so it behaves as an unannotated binding: members dispatch dynamically on the runtime value and is decides what it actually holds.

val x: Any = "s"
println(x is String)   // true

# List

List<T>

An ordered sequence, built by listOf. kotlinrs does not enforce read-only-ness — List and MutableList are the same runtime object, so listOf(1, 2).add(3) succeeds here where Kotlin rejects it at compile time. Prints as [a, b].

val xs: List<Int> = listOf(1, 2, 3)
println(xs)   // [1, 2, 3]

# MutableList

MutableList<T>

The mutable List annotation, built by mutableListOf / arrayListOf. It denotes the same runtime object as List; the distinction is documentation only.

val xs: MutableList<Int> = mutableListOf(1)
xs.add(2)
println(xs)   // [1, 2]

# Set

Set<T>

A distinct-element collection built by setOf. It keeps insertion order for display but compares order-insensitively, so setOf(1, 2) == setOf(2, 1) and a Set never equals a List. Prints as [a, b].

println(setOf(3, 1, 3))   // [3, 1]

# MutableSet

MutableSet<T>

The mutable Set annotation, built by mutableSetOf. add answers whether the element was new, which is what distinguishes it from a list's add.

val s: MutableSet<Int> = mutableSetOf(1)
println(s.add(1))   // false

# Map

Map<K, V>

A key/value association built by mapOf from k to v pairs. It is an insertion-ordered entry list, not a hash table — lookup is a linear scan under structural key equality, and iteration order is always insertion order even for hashMapOf. Prints as {k=v, k=v}.

val m: Map<String, Int> = mapOf("a" to 1)
println(m)   // {a=1}

# MutableMap

MutableMap<K, V>

The mutable Map annotation, built by mutableMapOf / hashMapOf. put, remove and indexed assignment all write through it.

val m: MutableMap<String, Int> = mutableMapOf()
m["a"] = 1
println(m)   // {a=1}

# Pair

Pair<A, B>

A two-element tuple, built only by the infix toPair(a, b) is not a resolvable constructor here. Its members are first and second, and it destructures through component1/component2.

val p: Pair<Int, String> = 1 to "one"
println(p.second)   // one

# Array

Array<T>

A JVM-style array. It inherits identity equality and the JVM's descriptor toString, so arrayOf(1, 2) prints as [Ljava.lang.Integer;@0 rather than showing its elements — use joinToString or toList to see them.

val a: Array<Int> = arrayOf(1, 2, 3)
println(a[1])              // 2
println(a.joinToString())  // 1, 2, 3

# IntArray

IntArray

A primitive Int array, descriptor [I. DoubleArray ([D), BooleanArray ([Z) and CharArray ([C) are the other three. All four share the sequence members with List.

val a: IntArray = IntArray(3)
println(a.sum())   // 0

# IntRange

IntRange

The value a..b builds — iterable, summable, and a receiver for the higher-order collection functions. A re-stepped or reversed range becomes an IntProgression, which prints in its a..b step n / a downTo b step n form.

val r: IntRange = 1..5
println(r.sum())        // 15
println(r.reversed())   // 5 downTo 1 step 1

# Nothing

Nothing

The bottom type, the static type Kotlin gives a throw. kotlinrs accepts the annotation but resolves no static type from it — like every non-primitive name it is carried as an opaque class name.

fun fail(): Nothing = throw IllegalStateException("no")
fun main() { println(try { fail() } catch (e: Exception) { "caught" }) }

Builtin Functions

# println

println()
println(value: Any?)

Writes a value to stdout followed by a newline, and returns Unit. It takes at most one argument. While an exception is unwinding it is suppressed, so nothing is emitted between a throw and its handler.

println(6 * 7)   // 42

# print

print()
print(value: Any?)

Writes a value to stdout with no trailing newline. Same one-argument limit and unwinding suppression as println.

print("a"); print("b")   // ab

# listOf

listOf(vararg elements: T): List<T>

Builds a List from its arguments. The result is a plain mutable heap list — kotlinrs does not enforce read-only-ness, so add on it succeeds.

val xs = listOf(1, 2, 3)
println(xs.size)   // 3

# mutableListOf

mutableListOf(vararg elements: T): MutableList<T>

Builds a mutable List. Identical to listOf at run time; the two differ only in what the annotation documents.

val xs = mutableListOf(1)
xs.add(2)
println(xs)   // [1, 2]

# arrayListOf

arrayListOf(vararg elements: T): MutableList<T>

The java.util.ArrayList spelling of mutableListOf. It builds the same heap list.

println(arrayListOf(1, 2))   // [1, 2]

# emptyList

emptyList(): List<T>

Builds an empty List. An explicit type argument (emptyList<Int>()) is accepted and ignored — typing here is coarse.

println(emptyList<Int>())   // []

# setOf

setOf(vararg elements: T): Set<T>

Builds a Set: duplicates are dropped on the way in, insertion order is kept for display, and equality is order-insensitive. This is Kotlin's LinkedHashSet-backed behaviour.

println(setOf(3, 1, 3))   // [3, 1]

# mutableSetOf

mutableSetOf(vararg elements: T): MutableSet<T>

Builds a mutable Set. add on it answers whether the element was new.

val s = mutableSetOf(1)
println(s.add(2))   // true

# hashSetOf

hashSetOf(vararg elements: T): MutableSet<T>

Builds a java.util.HashSet, which iterates its BUCKET TABLE rather than its insertion sequence — so the printed order is neither the argument order nor a sorted one. A key sits in bucket (n - 1) and (h xor (h ushr 16)) of a power-of-two table sized from the element count, and buckets are walked in index order. Use linkedSetOf for insertion order or sortedSetOf for ascending order.

println(hashSetOf(3, 1))   // [1, 3]

# linkedSetOf

linkedSetOf(vararg elements: T): MutableSet<T>

The LinkedHashSet spelling. Insertion-ordered, which is what every Set here already is.

println(linkedSetOf(2, 1))   // [2, 1]

# sortedSetOf

sortedSetOf(vararg elements: T): MutableSet<T>

Builds a TreeSet — the elements in ascending natural order, whatever order they were given in, and kept that way as more are added.

println(sortedSetOf(3, 1, 2))   // [1, 2, 3]

# emptySet

emptySet(): Set<T>

Builds an empty Set.

println(emptySet<Int>())   // []

# mapOf

mapOf(vararg pairs: Pair<K, V>): Map<K, V>

Builds a Map from k to v pairs. Entries stay in insertion order and keys are matched by structural equality on a linear scan.

val m = mapOf("a" to 1, "b" to 2)
println(m["a"])   // 1

# mutableMapOf

mutableMapOf(vararg pairs: Pair<K, V>): MutableMap<K, V>

Builds a mutable Map. put, remove and m[k] = v all write through it.

val m = mutableMapOf("a" to 1)
m["b"] = 2
println(m)   // {a=1, b=2}

# hashMapOf

hashMapOf(vararg pairs: Pair<K, V>): MutableMap<K, V>

Builds a java.util.HashMap, which iterates its BUCKET TABLE rather than its insertion sequence — the printed order follows the keys' hashes, not the argument order. Use mutableMapOf/LinkedHashMap when insertion order matters.

println(hashMapOf("b" to 1, "a" to 2))   // {a=2, b=1}

# HashMap

HashMap(): MutableMap<K, V> / HashMap(other: Map<K, V>): MutableMap<K, V>

The java.util.HashMap constructor — empty, or a copy of another map. It iterates in bucket order like hashMapOf, but a no-argument one starts from Java's default 16-bucket table where a sized builder starts from a table derived from its element count; the two mask differently, so the same keys can print in different orders.

val m = HashMap<String, Int>()
m["a"] = 1
println(m)   // {a=1}

# LinkedHashMap

LinkedHashMap(): MutableMap<K, V> / LinkedHashMap(other: Map<K, V>): MutableMap<K, V>

The insertion-ordered map constructor — the same collection mapOf/mutableMapOf build.

println(LinkedHashMap(mapOf("b" to 2)))   // {b=2}

# HashSet

HashSet(): MutableSet<T> / HashSet(other: Collection<T>): MutableSet<T>

The java.util.HashSet constructor — empty, or a copy of another collection's elements. Iterates in bucket order, like hashSetOf. Note the difference from the builder: HashSet(listOf(1)) copies the list, where hashSetOf(listOf(1)) builds a one-element set holding it.

println(HashSet(listOf(3, 1)))   // [1, 3]

# LinkedHashSet

LinkedHashSet(): MutableSet<T> / LinkedHashSet(other: Collection<T>): MutableSet<T>

The insertion-ordered set constructor — the same collection setOf/linkedSetOf build.

println(LinkedHashSet(listOf(3, 1)))   // [3, 1]

# TreeSet

TreeSet(): MutableSet<T> / TreeSet(other: Collection<T>): MutableSet<T>

The ascending-order set constructor, as sortedSetOf builds.

println(TreeSet(listOf(3, 1, 2)))   // [1, 2, 3]

# ArrayList

ArrayList(): MutableList<T> / ArrayList(other: Collection<T>): MutableList<T>

The java.util.ArrayList constructor — empty, or a copy of another collection. A list keeps position order however it was built, so it has no ordering surprise the way its Set/Map siblings do.

println(ArrayList(listOf(3, 1)))   // [3, 1]

# emptyMap

emptyMap(): Map<K, V>

Builds an empty Map, which prints as {}.

println(emptyMap<String, Int>())   // {}

# arrayOf

arrayOf(vararg elements: T): Array<T>

Builds a JVM-style array. The element values decide the descriptor at run time, so a boxed array prints as [Ljava.lang.Integer;@n and compares by identity.

val a = arrayOf(1, 2, 3)
println(a[1])   // 2

# intArrayOf

intArrayOf(vararg elements: Int): IntArray

Builds a primitive Int array from the given elements.

println(intArrayOf(1, 2).sum())   // 3

# doubleArrayOf

doubleArrayOf(vararg elements: Double): DoubleArray

Builds a primitive Double array.

println(doubleArrayOf(1.5, 2.5).sum())   // 4.0

# booleanArrayOf

booleanArrayOf(vararg elements: Boolean): BooleanArray

Builds a primitive Boolean array.

println(booleanArrayOf(true, false).size)   // 2

# charArrayOf

charArrayOf(vararg elements: Char): CharArray

Builds a primitive Char array.

println(charArrayOf('a', 'b').joinToString(""))   // ab

# IntArray

IntArray(size: Int): IntArray
IntArray(size: Int) { i -> … }: IntArray

Builds a zero-filled Int array of the given size, or fills each slot with the lambda applied to its index. A negative size raises NegativeArraySizeException.

println(IntArray(3).sum())              // 0
println(IntArray(3) { it * 2 }.sum())   // 6

# DoubleArray

DoubleArray(size: Int): DoubleArray
DoubleArray(size: Int) { i -> … }: DoubleArray

Builds a zero-filled Double array, or fills it from the index lambda.

println(DoubleArray(2).sum())   // 0.0

# BooleanArray

BooleanArray(size: Int): BooleanArray
BooleanArray(size: Int) { i -> … }: BooleanArray

Builds a zero-filled Boolean array, or fills it from the index lambda.

println(BooleanArray(3).size)   // 3

# CharArray

CharArray(size: Int): CharArray
CharArray(size: Int) { i -> … }: CharArray

Builds a zero-filled Char array, or fills it from the index lambda.

println(CharArray(2).size)   // 2

# Array

Array(size: Int) { i -> … }: Array<T>

Builds a generic array from an index lambda. Unlike the four primitive builders it exists *only* in the initializer form — Kotlin has no zero-filled Array(n) — and its descriptor is inferred from the elements the lambda produced.

println(Array(3) { it * 2 }.joinToString())   // 0, 2, 4

# StringBuilder

StringBuilder()
StringBuilder(text: CharSequence)
StringBuilder(capacity: Int)

A mutable character sequence. Every mutator (append, appendLine, insert, delete, replace, deleteCharAt, reverse, clear) answers the RECEIVER, so calls chain and keep building one object. setLength/setCharAt answer Unit; setLength pads with \u0000 when it grows. Content is held as UTF-16 code units, so length, sb[i], and every index argument count chars the way the JVM does. capacity() is modelled too: 16 by default, text.length + 16 from a text, and an append that does not fit grows to max(2 * cap + 2, needed). The read-only CharSequence members (length, indexOf, substring, startsWith, first, toList, …) behave exactly as on String. It overrides neither equals nor hashCode, so two builders holding the same text are NOT equal.

val sb = StringBuilder()
sb.append("a").append(1).append(true)
println(sb)          // a1true
println(sb.length)   // 6

# buildString

buildString(block: StringBuilder.() -> Unit): String
buildString(capacity: Int, block: StringBuilder.() -> Unit): String

Runs the block against a fresh StringBuilder bound as this — so append(x) needs no qualifier — and yields its toString(). The capacity overload is accepted and its hint discarded.

println(buildString { append("a"); append(1) })   // a1

# buildList

buildList(block: MutableList<T>.() -> Unit): List<T>

The list counterpart of buildString: a fresh mutable list bound as the block's this, yielded once the block has filled it. add/addAll inside need no qualifier.

println(buildList { add(1); addAll(listOf(2, 3)) })   // [1, 2, 3]

# listOfNotNull

listOfNotNull(vararg elements: T?): List<T>

Builds a List from its arguments with the nulls dropped. filterNotNull() is the member form, on any existing collection.

println(listOfNotNull(1, null, 3))   // [1, 3]

# repeat

repeat(times: Int, action: (Int) -> Unit)

Runs the block times times with the zero-based index as it, and yields Unit. A non-positive count runs it not at all.

repeat(3) { print(it) }   // 012

# require

require(value: Boolean)
require(value: Boolean, lazyMessage: () -> Any)
requireNotNull(value: T?): T

Argument preconditions. A false condition throws IllegalArgumentException: Failed requirement., or the message the block produced — the block runs ONLY on the failing path. requireNotNull throws Required value was null. on null and otherwise answers the value.

try { require(1 > 2) { "bad input" } } catch (e: Exception) { println(e.message) }   // bad input

# check

check(value: Boolean)
check(value: Boolean, lazyMessage: () -> Any)
checkNotNull(value: T?): T
error(message: Any): Nothing

State preconditions — the IllegalStateException mirror of require. The defaults are Check failed. and Required value was null.; error(msg) throws unconditionally with the message given.

try { check(false) } catch (e: Exception) { println(e.message) }   // Check failed.

# TODO

TODO(): Nothing
TODO(reason: String): Nothing

Throws kotlin.NotImplementedError: An operation is not implemented, with : reason appended when one is given. It is an Error, not an Exception, so catch (e: Exception) does not catch it.

try { TODO("later") } catch (e: Throwable) { println(e) }   // kotlin.NotImplementedError: An operation is not implemented: later

Math

# abs

abs(n: Int): Int
abs(x: Double): Double

Absolute value, keeping an Int result for an integral argument. Lives in kotlin.math, so it is unresolvable without import kotlin.math.abs or a star import — exactly as in Kotlin.

import kotlin.math.abs
fun main() { println(abs(-3)) }   // 3

# sqrt

sqrt(x: Double): Double

Square root, always Double. Needs the kotlin.math import.

import kotlin.math.sqrt
fun main() { println(sqrt(9.0)) }   // 3.0

# floor

floor(x: Double): Double

Largest Double no greater than the argument. Needs the kotlin.math import.

import kotlin.math.floor
fun main() { println(floor(-1.5)) }   // -2.0

# ceil

ceil(x: Double): Double

Smallest Double no less than the argument. Needs the kotlin.math import.

import kotlin.math.ceil
fun main() { println(ceil(1.2)) }   // 2.0

# round

round(x: Double): Double

Rounds to the closest integer as a Double, with **ties to even** — round(2.5) is 2.0 and round(3.5) is 4.0. This is kotlin.math.round; Math.round is the different, half-up, Long-returning one.

import kotlin.math.round
fun main() { println(round(2.5)) }   // 2.0

# max

max(a: Int, b: Int): Int
max(a: Double, b: Double): Double

Larger of two values, keeping an Int result when both are integral. Lives in kotlin.math and needs the import; maxOf is the auto-imported spelling of the same operation.

import kotlin.math.max
fun main() { println(max(2, 9)) }   // 9

# min

min(a: Int, b: Int): Int
min(a: Double, b: Double): Double

Smaller of two values, keeping an Int result when both are integral. Needs the kotlin.math import; minOf is the auto-imported spelling.

import kotlin.math.min
fun main() { println(min(2, 9)) }   // 2

# maxOf

maxOf(a: T, b: T): T

Larger of two values. It lives in the auto-imported kotlin package, so unlike max it needs no import; it dispatches to the same implementation.

println(maxOf(2, 9))   // 9

# minOf

minOf(a: T, b: T): T

Smaller of two values, auto-imported like maxOf and sharing min's implementation.

println(minOf(2, 9))   // 2

# PI

PI: Double

The kotlin.math circle constant, in scope only under the import. It folds to a literal at compile time rather than paying a host dispatch. Also reachable as Math.PI, which needs no import.

import kotlin.math.PI
fun main() { println(PI) }   // 3.141592653589793

# E

E: Double

The kotlin.math base of the natural logarithm, in scope only under the import, and also reachable as Math.E.

import kotlin.math.E
fun main() { println(E) }   // 2.718281828459045

# Math

Math.abs(…)  Math.max(…)  Math.min(…)
Math.sqrt(…)  Math.floor(…)  Math.ceil(…)
Math.round(…)  Math.PI  Math.E

The java.lang.Math statics. Kotlin auto-imports java.lang.* on the JVM, so these need no import line — which is the practical difference from the kotlin.math top-level spellings. A local binding or a user class named Math shadows the whole thing.

println(Math.abs(-3))   // 3

# Math.round

Math.round(x: Double): Long

The odd one out of the rounding family: **half-up** (floor(x + 0.5)) and returning a Long, where kotlin.math.round is ties-to-even and returns a Double. It dispatches under its own name for exactly that reason.

println(Math.round(2.5))   // 3

Throwables

# Throwable

Throwable()
Throwable(message: String)

The root of the modelled hierarchy — java.lang.Throwable. catch (e: Throwable) matches anything in flight, including a value that is not one of the sixteen built-in classes. A constructor with no argument leaves message null.

try { throw Throwable("boom") } catch (e: Throwable) { println(e.message) }   // boom

# Exception

Exception()
Exception(message: String)

java.lang.Exception, the parent of RuntimeException. Catching it catches every runtime exception below it but not an Error.

try { 1 / 0 } catch (e: Exception) { println("caught") }   // caught

# Error

Error()
Error(message: String)

java.lang.Error, a direct child of Throwable and a sibling of Exception — so catch (e: Exception) does *not* catch it.

try { throw Error("fatal") } catch (e: Throwable) { println("caught") }   // caught

# RuntimeException

RuntimeException()
RuntimeException(message: String)

java.lang.RuntimeException, the parent of every fault the runtime itself raises.

try { throw RuntimeException("x") } catch (e: RuntimeException) { println(e.message) }   // x

# ArithmeticException

ArithmeticException(message: String)

Raised by integer / and % when the divisor is zero, with the message / by zero. Floating-point division never raises it — it yields Infinity or NaN.

try { 1 / 0 } catch (e: ArithmeticException) { println(e.message) }   // / by zero

# IllegalArgumentException

IllegalArgumentException(message: String)

Raised by a negative take/drop/repeat count, a non-positive range step, and digitToInt on a non-digit Char.

try { listOf(1).take(-1) } catch (e: IllegalArgumentException) { println("caught") }   // caught

# IllegalStateException

IllegalStateException(message: String)

Never raised by the runtime itself — it is here for user code, most often as the right operand of ?:.

val x: Int? = null
try { x ?: throw IllegalStateException("missing") } catch (e: Exception) { println(e.message) }

# NumberFormatException

NumberFormatException(message: String)

A child of IllegalArgumentException. kotlinrs never raises it — there is no String.toInt to fail — so it exists for user code and for the hierarchy's shape.

try { throw NumberFormatException("bad") } catch (e: IllegalArgumentException) { println("caught") }

# IndexOutOfBoundsException

IndexOutOfBoundsException(message: String)

The parent of the string and array out-of-range classes. It is raised directly by removeAt past the end of a list and by an indexed write past the end.

try { mutableListOf(1).removeAt(5) } catch (e: IndexOutOfBoundsException) { println("caught") }

# StringIndexOutOfBoundsException

StringIndexOutOfBoundsException(message: String)

Raised by s[i] and substring when a UTF-16 offset falls outside the string.

try { "abc".substring(9) } catch (e: Exception) { println(e.message) }

# ArrayIndexOutOfBoundsException

ArrayIndexOutOfBoundsException(message: String)

Raised by an out-of-range indexed read on a List, Set or Array — note that the *method* form, get(i), raises NoSuchElementException instead, which diverges from Kotlin.

try { listOf(1, 2)[5] } catch (e: Exception) { println(e.message) }

# NullPointerException

NullPointerException()
NullPointerException(message: String)

Raised by !! on a null value. Since kotlinrs does no static null checking, !! is the only place the runtime produces one on its own.

val x: Int? = null
try { x!! } catch (e: NullPointerException) { println("caught") }

# ClassCastException

ClassCastException(message: String)

Modelled for the hierarchy but never raised: kotlinrs has no as cast operator, so there is no cast to fail.

try { throw ClassCastException("x") } catch (e: RuntimeException) { println("caught") }

# UnsupportedOperationException

UnsupportedOperationException(message: String)

Raised by reduce on an empty sequence, with the message Empty collection can't be reduced.

try { listOf<Int>().reduce { a, b -> a + b } } catch (e: Exception) { println("caught") }

# NegativeArraySizeException

NegativeArraySizeException(message: String)

Raised by an array builder given a negative size, such as IntArray(-1).

try { IntArray(-1) } catch (e: Exception) { println("caught") }

# NoSuchElementException

NoSuchElementException(message: String)

The one built-in throwable outside java.lang — it is java.util.NoSuchElementException, and the package is observable through toString. Raised by first, last, get, max and min on an empty sequence, with the message List is empty.

try { listOf<Int>().first() } catch (e: NoSuchElementException) { println(e.message) }

String Members

# length

String.length: Int

Number of UTF-16 code units — the JVM kotlin.String.length contract, not the Unicode scalar count. Every index, slice and indexOf result uses the same basis. A non-ASCII character written inside a string literal was split into UTF-8 bytes by the lexer, so its length is counted in those bytes.

println("abc".length)   // 3

# uppercase

String.uppercase(): String

Full Unicode uppercase mapping, so a character whose mapping expands does expand (ß becomes SS). No locale argument is accepted.

println("abc".uppercase())   // ABC

# toUpperCase

String.toUpperCase(): String

The deprecated Kotlin spelling of uppercase, accepted here and dispatching to the same implementation.

println("abc".toUpperCase())   // ABC

# lowercase

String.lowercase(): String

Full Unicode lowercase mapping. No locale argument is accepted.

println("ABC".lowercase())   // abc

# toLowerCase

String.toLowerCase(): String

The deprecated Kotlin spelling of lowercase, dispatching to the same implementation.

println("ABC".toLowerCase())   // abc

# trim

String.trim(): String

Strips leading and trailing whitespace, by Unicode's whitespace definition. There is no predicate or char-set overload, and no trimStart/trimEnd/trimIndent.

println("  hi  ".trim())   // hi

# isEmpty

String.isEmpty(): Boolean

True when the string has no code units at all.

println("".isEmpty())   // true

# isNotEmpty

String.isNotEmpty(): Boolean

The negation of isEmpty.

println("a".isNotEmpty())   // true

# isBlank

String.isBlank(): Boolean

True when the string is empty or made only of whitespace.

println("  ".isBlank())   // true

# isNotBlank

String.isNotBlank(): Boolean

The negation of isBlank.

println(" x ".isNotBlank())   // true

# contains

String.contains(other: Any): Boolean

Substring test. The argument is rendered by the same stringifier println uses, so a Char argument reads as its character — but an Int reads as its digits, which means "abc".contains(97) is false where the JVM's Char overload would say true. There is no ignoreCase parameter and no Regex overload.

println("abc".contains("bc"))   // true
println("abc".contains('b'))     // true

# startsWith

String.startsWith(prefix: Any): Boolean

Prefix test, with the argument rendered the same way contains renders it. No ignoreCase or start-offset parameter.

println("abc".startsWith("ab"))   // true

# endsWith

String.endsWith(suffix: Any): Boolean

Suffix test. No ignoreCase parameter.

println("abc".endsWith("bc"))   // true

# plus

String.plus(other: Any): String

The method spelling of + — what a + b compiles to on the JVM, and the form + has to take to be reached through a safe call.

println("ab".plus("c"))   // abc

# replace

String.replace(old: Any, new: Any): String

Replaces every occurrence of a literal substring. There is no Regex overload and no ignoreCase parameter.

println("a-b-c".replace("-", "+"))   // a+b+c

# repeat

String.repeat(n: Int): String

Concatenates the string with itself n times. n of zero yields the empty string; a negative n raises IllegalArgumentException: Count 'n' must be non-negative, but was N.

println("ab".repeat(2))   // abab

# indexOf

String.indexOf(needle: Any): Int

UTF-16 offset of the first occurrence, or -1 when there is none. There is no start-index parameter and no lastIndexOf.

println("abc".indexOf("c"))   // 2

# substring

String.substring(start: Int): String
String.substring(start: Int, end: Int): String

Slice between two UTF-16 offsets, end exclusive and defaulting to the length. An out-of-range or inverted pair raises StringIndexOutOfBoundsException: Range [start, end) out of bounds for length N.

println("hello".substring(1, 3))   // el

Char Members

# code

Char.code: Int

The character's UTF-16 code unit as an Int. The inverse is Int.toChar().

println('A'.code)   // 65

# digitToInt

Char.digitToInt(): Int

The decimal value of a digit character. Radix 10 only — no radix parameter — and a non-digit raises IllegalArgumentException: Char c is not a decimal digit. There is no digitToIntOrNull.

println('7'.digitToInt())   // 7

# isDigit

Char.isDigit(): Boolean

True for a Unicode numeric character. The classification delegates to Rust's Unicode tables, which agree with the JVM's Character over ASCII.

println('7'.isDigit())   // true

# isLetter

Char.isLetter(): Boolean

True for a Unicode alphabetic character. A lone surrogate half is neither letter nor digit, on this runtime and on the JVM alike.

println('a'.isLetter())   // true

# isLetterOrDigit

Char.isLetterOrDigit(): Boolean

True for a Unicode alphanumeric character.

println('_'.isLetterOrDigit())   // false

# isWhitespace

Char.isWhitespace(): Boolean

True for a Unicode whitespace character.

println(' '.isWhitespace())   // true

# isUpperCase

Char.isUpperCase(): Boolean

True for a Unicode uppercase character.

println('A'.isUpperCase())   // true

# isLowerCase

Char.isLowerCase(): Boolean

True for a Unicode lowercase character.

println('a'.isLowerCase())   // true

# uppercaseChar

Char.uppercaseChar(): Char

The uppercase mapping as a single Char. When the mapping would expand to more than one character the original is kept — the JVM's Character.toUpperCase(char) contract, so 'ß'.uppercaseChar() is still 'ß'.

println('a'.uppercaseChar())   // A

# lowercaseChar

Char.lowercaseChar(): Char

The lowercase mapping as a single Char, keeping the original when the mapping would expand.

println('A'.lowercaseChar())   // a

# uppercase

Char.uppercase(): String

The full uppercase mapping as a String, which is what lets an expanding mapping expand — unlike uppercaseChar.

println('a'.uppercase())   // A

# lowercase

Char.lowercase(): String

The full lowercase mapping as a String.

println('A'.lowercase())   // a

# compareTo

Char.compareTo(other: Char): Int

Orders two characters by code unit. It returns the sign only — -1, 0 or 1 — where the JVM returns the code-unit difference. The sign is all Kotlin's Comparable contract promises, but a program that reads the magnitude will see a different number here.

println('b'.compareTo('a'))   // 1

# plus

Char.plus(n: Int): Char

Displaces the code unit upward, yielding a Char. The result wraps into 16 bits.

println('A'.plus(1))   // B

# minus

Char.minus(n: Int): Char
Char.minus(other: Char): Int

Two behaviours chosen by the argument: subtracting an Int displaces the code unit and yields a Char; subtracting another Char yields the Int distance between them.

println('c'.minus(1))     // b
println('c'.minus('a'))   // 2

# equals

Char.equals(other: Any?): Boolean

Code-unit equality. A Char is a tagged handle rather than a heap object, so this needs no heap read.

println('a'.equals('a'))   // true

# hashCode

Char.hashCode(): Int

The code unit itself, matching the JVM's Character.hashCode.

println('A'.hashCode())   // 65

# toString

Char.toString(): String

The one-character string. The compiler resolves this statically from the receiver's type rather than through the generic stringifier, because the runtime representation alone could not tell a Char from a number.

println('A'.toString() + "!")   // A!

Sequence Members

# size

size: Int

Element count. Implemented once and shared by List, Set, Array and a range, so the four cannot drift apart. count() with no argument is the same call.

println(listOf(1, 2, 3).size)   // 3

# count

count(): Int

Element count, identical to size when called with no argument. With a lambda it is instead the predicate-counting higher-order function.

println((1..5).count())   // 5

# isEmpty

isEmpty(): Boolean

True when the sequence holds no elements.

println(emptyList<Int>().isEmpty())   // true

# isNotEmpty

isNotEmpty(): Boolean

The negation of isEmpty.

println(listOf(1).isNotEmpty())   // true

# first

first(): T

The first element. On a range it is instead a progression *property* — the start value, defined even when the range is empty. On a list, set or array an empty receiver raises NoSuchElementException: List is empty. There is no firstOrNull and no predicate overload.

println(listOf(10, 20).first())   // 10

# last

last(): T

The last element, or a range's end value as a progression property. An empty list, set or array raises NoSuchElementException. There is no lastOrNull.

println(listOf(10, 20).last())   // 20

# get

get(index: Int): T

Element at a position. Out of range it raises NoSuchElementException: List is empty. — which diverges from Kotlin, and from the [] operator on the same receiver, which raises ArrayIndexOutOfBoundsException.

println(listOf(10, 20).get(1))   // 20

# contains

contains(element: T): Boolean

Membership by the receiver's own equality rule. A List compares with equals; a Set reaches equals only after the hashes agree. So a data class element or a class with a declared equals is found by value, while a class that declares neither compares by reference identity and is not found by a fresh construction.

println(listOf(1, 2).contains(2))   // true

# containsAll

containsAll(elements: Collection<T>): Boolean

Whether every element of the argument is present, by the same rule contains uses. Vacuously true for an empty argument.

println(listOf(1, 2, 3).containsAll(listOf(1, 3)))   // true

# indexOf

indexOf(element: T): Int

Position of the first structurally equal element, or -1. There is no lastIndexOf and no indexOfFirst.

println(listOf("a", "b").indexOf("b"))   // 1

# sum

sum(): Int
sum(): Double

Adds the elements. The result is an Int when every element is integral and a Double otherwise — so a mixed sequence sums to a Double. An empty sequence sums to 0.

println(listOf(1, 2, 3).sum())   // 6

# average

average(): Double

Arithmetic mean, always a Double. An empty sequence averages to NaN rather than raising, matching Kotlin.

println(listOf(1, 2).average())   // 1.5

# max

max(): T

The largest element — strings lexicographically, chars by code unit, everything else numerically. An empty sequence raises NoSuchElementException. kotlinrs implements the pre-1.4 spelling only: there is no maxOrNull.

println(listOf(3, 1, 2).max())   // 3

# min

min(): T

The smallest element, by the same ordering as max, raising on an empty sequence. There is no minOrNull.

println(listOf(3, 1, 2).min())   // 1

# toList

toList(): List<T>

A List holding the same elements, in order. On a range this is what materializes it.

println((1..3).toList())   // [1, 2, 3]

# toMutableList

toMutableList(): MutableList<T>

The same copy toList makes — List and MutableList are one runtime object here.

println(setOf(1, 2).toMutableList())   // [1, 2]

# toTypedArray

toTypedArray(): Array<T>

Yields a List, not an Array — it shares toList's implementation. This diverges from Kotlin: the result prints its elements as [1, 2] and compares structurally, where a real array would print its JVM descriptor and compare by identity.

println(listOf(1, 2).toTypedArray())   // [1, 2]

# asList

asList(): List<T>

A List over the same elements. It is a copy here rather than a view, so mutating the receiver afterwards does not show through.

println(intArrayOf(1, 2).asList())   // [1, 2]

# toSet

toSet(): Set<T>

A Set of the distinct elements, first occurrence kept, in encounter order.

println(listOf(3, 1, 3).toSet())   // [3, 1]

# toMutableSet

toMutableSet(): MutableSet<T>

The same distinct Set toSet builds.

println(listOf(1, 1).toMutableSet())   // [1]

# toHashSet

toHashSet(): MutableSet<T>

The same insertion-ordered distinct Set. Kotlin's HashSet gives no order guarantee; here encounter order is always kept.

println(listOf(2, 1, 2).toHashSet())   // [2, 1]

# distinct

distinct(): List<T>

The distinct elements as a **List**, where toSet gives the same elements as a Set. That is the whole difference between the two, and it decides how the result compares and prints.

println(listOf(3, 1, 3).distinct())   // [3, 1]

# plus

plus(element: T): List<T> / plus(elements: Iterable<T>): List<T>

The receiver's elements followed by the argument's, as a collection of the receiver's own kind. Spelled + as an operator. The Iterable overload wins whenever the argument is one, so listOf(listOf(1)) + listOf(2) is [[1], 2]; use plusElement to append a collection whole. A Map receiver takes a Pair, another Map, or an iterable of pairs, and an existing key keeps its position while its value is replaced.

println(listOf(1, 2) + 3)   // [1, 2, 3]

# minus

minus(element: T): List<T> / minus(elements: Iterable<T>): List<T>

The receiver without the argument's elements, as a collection of the receiver's own kind. Spelled - as an operator. The element form drops only the FIRST match — listOf(1, 2, 2, 3) - 2 is [1, 2, 3] — while the Iterable form drops every occurrence of every listed element. A Map receiver takes a key or an iterable of keys.

println(listOf(1, 2, 3) - 2)   // [1, 3]

# plusElement

plusElement(element: T): List<T>

The receiver's elements followed by the argument as ONE element, pinning the overload plus would otherwise resolve to the Iterable form for a collection argument.

println(listOf(1, 2).plusElement(listOf(3)))   // [1, 2, [3]]

# minusElement

minusElement(element: T): List<T>

The receiver without the first element equal to the argument, treating it as one element rather than a sequence to subtract.

println(listOf(1, 2, 2).minusElement(2))   // [1, 2]

# union

union(other: Iterable<T>): Set<T>

Distinct elements of the receiver followed by those of the argument, as a Set. Defined on any iterable receiver, and always returning a Set whatever kind the receiver was. Only the method form parses — a union b as an infix call is a syntax error.

println(listOf(1, 2).union(listOf(2, 3)))   // [1, 2, 3]

# intersect

intersect(other: Iterable<T>): Set<T>

Distinct receiver elements that also appear in the argument, as a Set.

println(listOf(1, 2, 3).intersect(listOf(2, 3, 4)))   // [2, 3]

# subtract

subtract(other: Iterable<T>): Set<T>

Distinct receiver elements that do not appear in the argument, as a Set.

println(listOf(1, 2, 3).subtract(listOf(2)))   // [1, 3]

# sorted

sorted(): List<T>

Ascending sort into a new List — strings lexicographically, chars by code unit, everything else numerically. sortedWith takes an explicit ordering; there is no in-place sort.

println(listOf(3, 1, 2).sorted())   // [1, 2, 3]

# sortedDescending

sortedDescending(): List<T>

Descending sort into a new List.

println(listOf(3, 1, 2).sortedDescending())   // [3, 2, 1]

# take

take(n: Int): List<T>

The first n elements as a List. An oversized n clamps to the whole sequence; a negative one raises IllegalArgumentException: Requested element count N is less than zero. See takeLast for the other end and takeWhile for the predicate form.

println(listOf(1, 2, 3).take(2))   // [1, 2]

# takeLast

takeLast(n: Int): List<T>

The LAST n elements, with the same clamping and the same negative-count error as take. On a String receiver it answers a String, not a List<Char>.

println(listOf(1, 2, 3).takeLast(2))   // [2, 3]
println("abcde".takeLast(2))            // de

# drop

drop(n: Int): List<T>

Everything after the first n elements, as a List. An oversized n yields an empty list; a negative one raises IllegalArgumentException. See dropLast for the other end and dropWhile for the predicate form.

println(listOf(1, 2, 3).drop(1))   // [2, 3]

# dropLast

dropLast(n: Int): List<T>

Everything but the last n elements — the complement of takeLast at the same cut. On a String receiver it answers a String.

println(listOf(1, 2, 3).dropLast(1))   // [1, 2]
println("abcde".dropLast(2))            // abc

# generateSequence

generateSequence(seed: T?, next: (T) -> T?): Sequence<T>

A LAZY sequence: next runs on demand and ends the sequence by answering null. It is the one sequence here that is not materialized up front, because it is the one that can be endless — generateSequence(1) { it * 2 } is bounded only by a take, a takeWhile, or a short-circuiting search. map/filter/filterNot/takeWhile/dropWhile/take/drop stay lazy on it; anything else pulls the pipeline into a List first, which on an unbounded one raises rather than hanging.

println(generateSequence(1) { it * 2 }.take(5).toList())                     // [1, 2, 4, 8, 16]
println(generateSequence(1) { if (it < 20) it * 2 else null }.toList())      // [1, 2, 4, 8, 16, 32]

# splitToSequence

String.splitToSequence(vararg delimiters: String): Sequence<String>

split answering a sequence. The receiver bounds the result either way, so it is materialized like every other finite sequence here.

println("a,b,c".splitToSequence(",").toList())   // [a, b, c]

# unzip

unzip(): Pair<List<A>, List<B>>

zip run backwards: a sequence of pairs becomes a pair of sequences. A non-pair element is an error.

println(listOf(1 to "a", 2 to "b").unzip())   // ([1, 2], [a, b])

# zipWithNext

zipWithNext(): List<Pair<T, T>>
zipWithNext(transform: (T, T) -> R): List<R>

Pairs each element with its SUCCESSOR, so the result is one shorter than the receiver and empty for a receiver of one or none. The lambda overload transforms each such pair instead of yielding it.

println(listOf(1, 2, 3).zipWithNext())                  // [(1, 2), (2, 3)]
println(listOf(1, 2, 3).zipWithNext { a, b -> a + b })   // [3, 5]

# joinToString

joinToString(): String
joinToString(separator: Any): String

Renders the elements with the same stringifier println uses, joined by a separator that defaults to ", ". The positional prefix, postfix, limit and truncated parameters are honoured, and a trailing lambda transforms each element.

println(listOf(1, 2, 3).joinToString("-"))   // 1-2-3

# reversed

reversed(): List<T>
IntRange.reversed(): IntProgression

Reverses the order. On a list, set or array the result is a reversed List; on a range it is instead a descending IntProgression, which prints in its b downTo a step 1 form.

println(listOf(1, 2, 3).reversed())   // [3, 2, 1]
println((1..3).reversed())            // 3 downTo 1 step 1

Mutable Collection Members

# add

MutableList.add(element: T): Boolean
MutableSet.add(element: T): Boolean

Appends to a list, always answering true; on a set it inserts only when the element is new and answers whether it was. That difference in the answer is Kotlin's contract and is why one implementation serves both.

val s = mutableSetOf(1)
println(s.add(1))   // false
println(s.add(2))   // true

# remove

MutableList.remove(element: T): Boolean
MutableSet.remove(element: T): Boolean
MutableMap.remove(key: K): Boolean

Removes the first structurally equal element (or the entry under the key) and answers whether anything was removed. On a Map this diverges from Kotlin, which answers the removed *value* or null; here it is always a Boolean.

val xs = mutableListOf(1, 2)
println(xs.remove(1))   // true
println(xs)             // [2]

# removeAt

MutableList.removeAt(index: Int): T

Removes the element at a position and answers it. Out of range it raises IndexOutOfBoundsException. Lists only — a set has no positional removal.

val xs = mutableListOf(1, 2)
println(xs.removeAt(0))   // 1

# addAll

MutableCollection<T>.addAll(elements: Collection<T>): Boolean
MutableCollection<T>.removeAll(elements: Collection<T>): Boolean
MutableCollection<T>.retainAll(elements: Collection<T>): Boolean

Bulk mutation. Each answers whether the receiver CHANGED, so addAll(emptyList()) is false. A MutableList appends every element it is given; a MutableSet skips the ones it already holds.

val xs = mutableListOf(1, 2)
println(xs.addAll(listOf(3)))   // true
println(xs)                    // [1, 2, 3]

Map Members

# size

Map.size: Int

Number of entries.

println(mapOf("a" to 1, "b" to 2).size)   // 2

# isEmpty

Map.isEmpty(): Boolean

True when the map has no entries.

println(emptyMap<String, Int>().isEmpty())   // true

# isNotEmpty

Map.isNotEmpty(): Boolean

The negation of isEmpty.

println(mapOf("a" to 1).isNotEmpty())   // true

# containsKey

Map.containsKey(key: K): Boolean

Whether a structurally equal key is present. It is a linear scan of the entry list, not a hash lookup. There is no containsValue.

println(mapOf("a" to 1).containsKey("a"))   // true

# get

Map.get(key: K): V?

The value under a key, or null when the key is absent — the same lookup m[k] performs. It never raises. getOrElse(key) { … } supplies a fallback; there is no getOrDefault.

println(mapOf("a" to 1).get("z"))   // null

# keys

Map.keys: Set<K>

The keys in the map's iteration order, as a Set — so it compares order-insensitively against another Set, as Kotlin's does.

println(mapOf("a" to 1, "b" to 2).keys)   // [a, b]

# values

Map.values: List<V>

The values in the map's iteration order, as a List. entries yields the key/value entries themselves, which print as k=v.

println(mapOf("a" to 1, "b" to 2).values)   // [1, 2]

# toList

Map.toList(): List<Pair<K, V>>

The entries as Pairs, in iteration order. The pairs print (k, v) where the entries themselves print k=v, which is the whole reason this is not the generic sequence toList.

println(mapOf(1 to "a", 2 to "b").toList())   // [(1, a), (2, b)]

# filterKeys

Map.filterKeys(predicate: (K) -> Boolean): Map<K, V>

The entries whose KEY satisfies the predicate. The lambda sees the key alone — not the entry, as mapKeys does.

println(mapOf(1 to "a", 2 to "b").filterKeys { it > 1 })   // {2=b}

# filterValues

Map.filterValues(predicate: (V) -> Boolean): Map<K, V>

The entries whose VALUE satisfies the predicate, with the lambda seeing the value alone.

println(mapOf(1 to "a", 2 to "b").filterValues { it == "a" })   // {1=a}

# toSortedMap

Map.toSortedMap(): Map<K, V>

A java.util.TreeMap over the same entries: ascending natural key order, kept across later writes rather than applied once.

println(mapOf(2 to "b", 1 to "a").toSortedMap())   // {1=a, 2=b}

# put

MutableMap.put(key: K, value: V): V?

Sets a key, appending the entry when it is new. It answers the previous value, or null when there was none — the same write m[k] = v performs.

val m = mutableMapOf("a" to 1)
println(m.put("a", 2))   // 1

# remove

MutableMap.remove(key: K): Boolean

Drops the entry under a key. It answers a Boolean — whether anything was removed — where Kotlin answers the removed value or null.

val m = mutableMapOf("a" to 1)
println(m.remove("a"))   // true

Pair Members

# first

Pair.first: A

The left half of a Pair. Also reachable as component1(), which is what destructuring uses.

println((1 to "one").first)   // 1

# second

Pair.second: B

The right half of a Pair, also reachable as component2().

println((1 to "one").second)   // one

Numeric Members

# plus

Int.plus(other: Int): Int
Double.plus(other: Double): Double

The method spelling of + — the form the operator has to take to be reached through a safe call, as in count?.plus(1). Two integral operands wrap; any Double operand makes the result Double.

println(2.plus(3))   // 5

# minus

Int.minus(other: Int): Int
Double.minus(other: Double): Double

The method spelling of -.

println(5.minus(2))   // 3

# times

Int.times(other: Int): Int
Double.times(other: Double): Double

The method spelling of *, wrapping on two integral operands.

println(6.times(7))   // 42

# div

Int.div(other: Int): Int
Double.div(other: Double): Double

The method spelling of /. Two integral operands truncate toward zero and a zero divisor raises ArithmeticException: / by zero; a Double operand switches to IEEE division.

println(7.div(2))     // 3
println(2.5.div(2.0)) // 1.25

# rem

Int.rem(other: Int): Int
Double.rem(other: Double): Double

The method spelling of %, taking the dividend's sign for integers and raising on a zero integral divisor.

println(7.rem(2))   // 1

# toDouble

Int.toDouble(): Double
Double.toDouble(): Double

Widens to Double, which is what makes a following / divide in IEEE rather than truncate.

println(3.toDouble())   // 3.0

# toInt

Int.toInt(): Int
Long.toInt(): Int
Double.toInt(): Int
String.toInt(): Int

Narrows to 32 bits. From a Long that is a truncation of the low 32 bits (2147483648L.toInt() is -2147483648); from a Double it truncates toward zero and then saturates at the Int bounds; from a String it parses the text.

println(3.9.toInt())            // 3
println(2147483648L.toInt())    // -2147483648

# toLong

Int.toLong(): Long
Double.toLong(): Long
String.toLong(): Long

Widens to 64 bits, which stops the surrounding arithmetic from being narrowed back to Int. From a Double it truncates toward zero and saturates at the Long bounds.

println(3.9.toLong())                // 3
println(2147483647.toLong() + 1L)    // 2147483648

# toShort

Int.toShort(): Short

Truncates to the low 16 bits, signed — so 70000.toShort() is 4464. The result promotes back to Int for arithmetic, as Kotlin's does.

println(32768.toShort())   // -32768

# toByte

Int.toByte(): Byte

Truncates to the low 8 bits, signed — so 200.toByte() is -56. The result promotes back to Int for arithmetic, as Kotlin's does.

println(200.toByte())   // -56

# toChar

Int.toChar(): Char

The Char for the low 16 bits of the receiver — the inverse of Char.code.

println(65.toChar())   // A

Universal & Generated Members

# toString

Any.toString(): String

Renders any receiver as Kotlin would print it: a Double keeps its .0, null reads as null, a List as [a, b], a Map as {k=v}, an array as its JVM descriptor, and a data class as Name(p=v, …). In a program that overrides toString, calls route through a re-entrant display builtin so the override is what runs.

println(listOf(1, 2).toString())   // [1, 2]

# hashCode

Any.hashCode(): Int

An order-independent structural hash over a heap object. Two structurally equal values hash equal — the property a data class's generated hashCode needs — but the numbers themselves are not the JVM's.

data class Pt(val x: Int, val y: Int)
fun main() { println(Pt(1, 2).hashCode() == Pt(1, 2).hashCode()) }   // true

# equals

Any.equals(other: Any?): Boolean

The method spelling of ==, with the same structural rules — including a data class comparing only its primary-constructor properties and an Array comparing by identity.

println(listOf(1).equals(listOf(1)))   // true

# componentN

component1(): T  component2(): T  …

Positional accessors, 1-based, used by val (a, b) = expr destructuring. Defined on a data-class instance (over its primary-constructor properties, skipping inherited fields), a List, a Set, an Array and a Pair. Destructuring works in a val declaration only — a for ((k, v) in …) header is a parse error here.

data class Pt(val x: Int, val y: Int)
fun main() { val (a, b) = Pt(1, 2); println("$a $b") }   // 1 2

# copy

copy(vararg overrides: Any): T

A data class's generated clone-with-overrides. The arguments are **positional**, overriding the leading properties in declaration order — kotlinrs has no named arguments, so p.copy(y = 9) is not available; p.copy(9) overrides the first property. It calls the primary constructor, so a data class under a superclass re-runs its : Super(args) header.

data class Pt(val x: Int, val y: Int)
fun main() { println(Pt(1, 2).copy(9)) }   // Pt(x=9, y=2)

# message

Throwable.message: String?

The message a throwable was constructed with, or null when it was constructed without one.

try { 1 / 0 } catch (e: Exception) { println(e.message) }   // / by zero

Higher-Order Collection Functions

# map

map(transform: (T) -> R): List<R>

Applies the lambda to every element and collects the results into a List. The receiver may be a List, Set, Array, range or String — a range materializes first, which is what makes (1..3).map { … } work, and a String iterates its characters. A Pair receiver is an unresolved reference.

println(listOf(1, 2, 3).map { it * 2 })   // [2, 4, 6]

# mapIndexed

mapIndexed(transform: (Int, T) -> R): List<R>

Like map, but the lambda takes the element's index first and the element second. forEachIndexed, filterIndexed and flatMapIndexed take the same index-first shape.

println(listOf(1, 2, 3).mapIndexed { i, v -> i * v })   // [0, 2, 6]

# flatMap

flatMap(transform: (T) -> Iterable<R>): List<R>

Applies the lambda to every element and splices each iterable result into one flat List. A result that is not iterable contributes nothing rather than raising.

println(listOf(1, 2).flatMap { listOf(it, it) })   // [1, 1, 2, 2]

# flatMapIndexed

flatMapIndexed(transform: (Int, T) -> Iterable<R>): List<R>

Like flatMap, but the lambda takes the element's index first and the element second.

println(listOf(1, 2).flatMapIndexed { i, v -> listOf(i, v) })   // [0, 1, 1, 2]

# mapNotNull

mapNotNull(transform: (T) -> R?): List<R>

Like map, but a result that is null is dropped instead of collected.

println(listOf(1, 2, 3).mapNotNull { if (it > 1) it else null })   // [2, 3]

# onEach

onEach(action: (T) -> Unit): C

Runs the lambda once per element for its side effect and answers the RECEIVER, so it chains where forEach (which answers Unit) cannot. On a String receiver the result is the string.

println(listOf(1, 2).onEach { print(it) }.size)   // 122

# runningFold

runningFold(initial: R, operation: (R, T) -> R): List<R>

Like fold, but collects every intermediate accumulator INCLUDING the initial one, so the result is one longer than the receiver. scan is the same function under its other name.

println(listOf(1, 2, 3).runningFold(0) { a, n -> a + n })   // [0, 1, 3, 6]

# runningReduce

runningReduce(operation: (T, T) -> T): List<T>

Like reduce, but collects every intermediate accumulator. The first element seeds it, so an empty receiver answers an empty list rather than raising.

println(listOf(1, 2, 3).runningReduce { a, n -> a + n })   // [1, 3, 6]

# withIndex

withIndex(): List<IndexedValue<T>>

Pairs every element with its position. Each entry is an IndexedValue whose index and value are read as ordinary properties, which is what makes it print as IndexedValue(index=0, value=a) rather than as a Pair.

println("ab".withIndex().map { it.index.toString() + it.value })   // [0a, 1b]

# filter

filter(predicate: (T) -> Boolean): List<T>

Keeps the elements whose predicate returns exactly true; a null or non-Boolean result counts as false. The result is always a List, even from a Set receiver.

println(listOf(1, 2, 3, 4).filter { it % 2 == 0 })   // [2, 4]

# filterNot

filterNot(predicate: (T) -> Boolean): List<T>

Keeps the elements whose predicate does *not* hold. There is no filterNotNull or filterIsInstance.

println(listOf(1, 2, 3).filterNot { it > 2 })   // [1, 2]

# forEach

forEach(action: (T) -> Unit): Unit

Runs the lambda once per element for its side effect and yields Unit.

listOf(1, 2).forEach { print(it) }   // 12

# fold

fold(initial: R, operation: (R, T) -> R): R

Threads an accumulator through the sequence left to right, starting from the given initial value. The lambda takes the accumulator first. It is the one higher-order function here that takes a non-lambda argument as well.

println(listOf(1, 2, 3).fold(0) { acc, n -> acc + n })   // 6

# foldRight

foldRight(initial: R, operation: (T, R) -> R): R

fold from the END, and — the part that catches people — its lambda takes the ELEMENT first and the accumulator second, the opposite of fold. Both differences show at once on the same lambda.

println(listOf(1, 2, 3).foldRight("") { a, b -> "$a$b" })   // 123

# reduce

reduce(operation: (T, T) -> T): T

Like fold but seeded with the first element instead of an explicit initial. An empty receiver raises UnsupportedOperationException: Empty collection can't be reduced.

println(listOf(1, 2, 3).reduce { a, b -> a * b })   // 6

# reduceRight

reduceRight(operation: (T, T) -> T): T

reduce from the end: seeded with the LAST element, walking backwards, with the lambda taking (element, acc). Same error on an empty receiver.

println(listOf(1, 2, 3).reduceRight { a, b -> a - b })   // 2

# any

any(predicate: (T) -> Boolean): Boolean

True as soon as one element satisfies the predicate, short-circuiting on the first hit. The no-argument any() overload is not implemented — use isNotEmpty().

println(listOf(1, 2, 3).any { it > 2 })   // true

# all

all(predicate: (T) -> Boolean): Boolean

True when every element satisfies the predicate, short-circuiting on the first failure. Vacuously true for an empty receiver.

println(listOf(2, 4).all { it % 2 == 0 })   // true

# none

none(predicate: (T) -> Boolean): Boolean

True when no element satisfies the predicate, short-circuiting on the first hit.

println(listOf(1, 2).none { it > 5 })   // true

# count

count(predicate: (T) -> Boolean): Int

How many elements satisfy the predicate. Called with no argument, count() is instead the sequence member that reports size.

println(listOf(1, 2, 3).count { it > 1 })   // 2

# sumOf

sumOf(selector: (T) -> Int): Int
sumOf(selector: (T) -> Double): Double

Sums the lambda's results, yielding an Int when every one is integral and a Double otherwise — the same rule sum() uses.

println(listOf("ab", "c").sumOf { it.length })   // 3

# maxByOrNull

maxByOrNull(selector: (T) -> R): T?

The element whose selector value is largest, or null when the receiver is empty. Ties keep the first such element. The selector runs once per element.

println(listOf("a", "abc").maxByOrNull { it.length })   // abc

# minByOrNull

minByOrNull(selector: (T) -> R): T?

The element whose selector value is smallest, or null on an empty receiver, keeping the first of any tie.

println(listOf("abc", "a").minByOrNull { it.length })   // a

# sortedBy

sortedBy(selector: (T) -> R): List<T>

Ascending sort by the selector's value, evaluated once per element and stable — equal keys keep their input order.

println(listOf("abc", "a").sortedBy { it.length })   // [a, abc]

# sortedByDescending

sortedByDescending(selector: (T) -> R): List<T>

Descending sort by the selector's value. The comparison is flipped rather than the result reversed, so ties still come out in input order as Kotlin requires.

println(listOf("a", "abc").sortedByDescending { it.length })   // [abc, a]

# sortedWith

sortedWith(comparator: Comparator<T>): List<T>
sortedWith(compare: (T, T) -> Int): List<T>

Stable sort under an explicit ordering — either a Comparator from compareBy or a plain two-argument lambda answering a negative/zero/positive Int.

println(listOf(3, 1, 2).sortedWith(compareBy { it }))       // [1, 2, 3]
println(listOf(3, 1, 2).sortedWith { a, b -> b - a })       // [3, 2, 1]

# compareBy

compareBy(vararg selectors: (T) -> R): Comparator<T>

Builds a Comparator from one or more key selectors, compared left to right with the first non-equal key deciding. Extend it with thenBy/thenByDescending.

println(listOf(3, 1, 2).sortedWith(compareBy { it }))   // [1, 2, 3]

# compareByDescending

compareByDescending(vararg selectors: (T) -> R): Comparator<T>

compareBy with every key reversed.

println(listOf(1, 3, 2).sortedWith(compareByDescending { it }))   // [3, 2, 1]

# thenBy

Comparator<T>.thenBy(selector: (T) -> R): Comparator<T>

A NEW comparator with one more key appended as a tiebreak — the receiver is unchanged, as Kotlin's immutable comparators are. The added key is consulted only when every earlier key ties.

val byLen = compareBy<String> { it.length }
println(listOf("cc", "a", "bb").sortedWith(byLen.thenBy { it }))   // [a, bb, cc]

# thenByDescending

Comparator<T>.thenByDescending(selector: (T) -> R): Comparator<T>

thenBy with the added key reversed. Each key carries its own direction, so an ascending key can be broken by a descending one.

val byLen = compareBy<String> { it.length }
println(listOf("cc", "a", "bb").sortedWith(byLen.thenByDescending { it }))   // [a, cc, bb]

# associate

associate(transform: (T) -> Pair<K, V>): Map<K, V>

Builds a Map from the Pair each lambda call returns. A lambda result that is not a Pair raises kotlin: associate expects a Pair. Later duplicate keys overwrite earlier ones.

println(listOf(1, 2).associate { it to it * it })   // {1=1, 2=4}

# associateBy

associateBy(keySelector: (T) -> K): Map<K, T>

Builds a Map whose keys are the lambda's results and whose values are the elements — the mirror image of associateWith.

println(listOf("ab", "c").associateBy { it.length })   // {2=ab, 1=c}

# associateWith

associateWith(valueSelector: (T) -> V): Map<T, V>

Builds a Map keyed by the elements, with the lambda's results as the values.

println(listOf(1, 2, 3).associateWith { it * 2 })   // {1=2, 2=4, 3=6}

# groupBy

groupBy(keySelector: (T) -> K): Map<K, List<T>>

Buckets the elements by the lambda's result. Keys appear in first-encounter order and each bucket keeps its elements in input order.

println(listOf(1, 2, 3, 4).groupBy { it % 2 })   // {1=[1, 3], 0=[2, 4]}

# groupingBy

groupingBy(keySelector: (T) -> K): Grouping<T, K>

Pairs the source with a key selector and does no work yet — the result is consumed by a terminal operation such as eachCount. That laziness is the difference from groupBy, which materializes the per-key lists.

println(listOf(1, 2, 3, 4).groupingBy { it % 2 }.eachCount())   // {1=2, 0=2}

# eachCount

eachCount(): Map<K, Int>

How many elements fell under each key of a Grouping. The keys come out in first-encounter order, as the LinkedHashMap it fills.

println(listOf("a", "bb", "cc").groupingBy { it.length }.eachCount())   // {1=1, 2=2}

Scope Functions

# let

T.let(block: (T) -> R): R

Runs the block with the receiver bound to it and yields the block's result. Works on any receiver, not just a collection. Paired with a safe call (x?.let { … }) it is the null-guard idiom.

println(listOf(1, 2).let { it.size })   // 2

# also

T.also(block: (T) -> Unit): T

Runs the block with the receiver bound to it for its side effect and yields the **receiver**, not the block's result — which is what makes it chainable mid-expression.

println(listOf(1, 2).also { print(it.size) })   // 2[1, 2]

# takeIf

T.takeIf(predicate: (T) -> Boolean): T?

Yields the receiver when the predicate returns true, and null otherwise.

println(5.takeIf { it > 3 })   // 5
println(2.takeIf { it > 3 })   // null

# takeUnless

T.takeUnless(predicate: (T) -> Boolean): T?

The negation of takeIf: yields the receiver when the predicate returns false, and null otherwise.

println(5.takeUnless { it > 3 })   // null
println(2.takeUnless { it > 3 })   // 2

# run

T.run(block: T.() -> R): R
run(block: () -> R): R

Runs the block with the receiver bound to **this** — so the receiver's members are reachable without a qualifier — and yields the block's result. The receiverless form run { … } is a block evaluated on the spot for its value.

println("abc".run { length })   // 3
println(run { 1 + 2 })         // 3

# apply

T.apply(block: T.() -> Unit): T

Runs the block with the receiver bound to **this** for its side effect and yields the **receiver** — the configure-then-return idiom. also is the same shape with the receiver as it instead.

class Box(var w: Int)
println(Box(1).apply { w = 5 }.w)   // 5

# with

with(receiver: T, block: T.() -> R): R

The free-function spelling of run: the argument becomes the block's this, and the block's result is the value.

println(with("hello") { uppercase() + length })   // HELLO5

Result

# runCatching

runCatching(block: () -> T): Result<T>

Runs the block and packages its outcome: Success(v) for a normal return, Failure(<throwable>) for a throw — including the runtime faults this frontend raises, so runCatching { 1 / 0 } is a failure rather than a halt.

println(runCatching { 6 / 2 })   // Success(3)
println(runCatching { 1 / 0 }.isFailure)   // true

# getOrNull

Result<T>.getOrNull(): T?

The success value, or null on failure. exceptionOrNull() is its mirror — the throwable, or null on success.

println(runCatching { 6 / 2 }.getOrNull())   // 3
println(runCatching { 1 / 0 }.getOrNull())   // null

# getOrElse

Result<T>.getOrElse(onFailure: (Throwable) -> T): T

The success value, or the block applied to the throwable. The block does not run at all on success.

println(runCatching { 1 / 0 }.getOrElse { -1 })   // -1

# isSuccess

Result<T>.isSuccess: Boolean
Result<T>.isFailure: Boolean

Which branch of the union the result holds. onSuccess/onFailure run a block for the matching branch and yield the result unchanged; map transforms a success and passes a failure through.

println(runCatching { 6 / 2 }.isSuccess)   // true
println(runCatching { 6 / 2 }.map { it + 1 })   // Success(4)

More