Imagine you’re writing universal domain logic for your program. All I/O is provided via an interface (like UI, persistent storage, networking, etc.), you don’t have to think about it, and you almost don’t care about the platform, on which you run. Your program is fairly simple, only a few straightforward tasks. This chapter describes all essentials you need to write it. The foundational basics that you can build upon: data, processing primitives, and functions to reuse code.
Data
At the core of any program, lies the data. The sole purpose is to often receive and transform, sometimes create, but always output the data. A program that doesn’t output anything is useless. Take away variables (or functions that act as variables) and there’s nothing to process. In this section, cornerstone data representations are discussed: primitive data types, variables, collections, ranges, text, optionals, and function types.
Note: Custom data types (structs, classes) are showcased later, in the Abstractions chapter. They help a lot and make your program much easier to reason about, but you don’t absolutely need them. You can get away by structuring your data using collections, Map a.k.a. Dictionary a.k.a. Associative Array in particular. This has been demonstrated by Lua scripting language, for example.
Function types
Functions are first-class citizens and can be stored in a variable:
// Lambda
var fn: (Int, String, Boolean) -> Double =
{ param1: Int, param2, param3 -> 42.0 }
// Anonymous function
var annotatedFn: (param1: Int, param2: String, param3: Boolean) -> Double =
fun(param1: Int, param2: String, param3: Boolean): Double {
return 42.0
}
fn = annotatedFn
annotatedFn = fn
Anonymous functions can specify the return type and create a scope for the return statement, same as in a regular function. return works differently for lambdas, see following sections.
Simplest possible function signature:
val simpleFn: () -> Unit = {
// Side-effect code goes here, since this function doesn't return anything.
// Also, there are no parameters.
}
Functions with a receiver, a.k.a. extension functions:
var fnWithReceiver: String.(Int) -> String = String::drop
var fnEquivalentWithoutReceiver: (String, Int) -> String = fnWithReceiver
fnWithReceiver = fnEquivalentWithoutReceiver
val light = "flight".fnWithReceiver(1)
val alsoLight = fnWithReceiver("flight", 1)
Lambdas with a single parameter can omit the parameter name and use the implicit it shorthand:
val implicitLambdaParameter: (Int) -> Int = { it + 1 }