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.
Optionals
A variable can be optional and have a value of null:
var maybeText: String? = null
maybeText = "A message"
Optional unwrapping:
var maybeUppercaseText = maybeText?.substring(2)?.uppercase()
maybeUppercaseText = maybeText?.let { it.substring(2).uppercase() }
Fallback value (via so called Elvis operator):
val uppercaseText = maybeUppercaseText ?: "EMPTY"
Forced unwrapping:
val messageGuaranteed = maybeText!!
val lengthGuaranteed = maybeText!!.length
Safe type-casting that succeeds only when the underlying type matches the specified type, otherwise returns null:
val typeErasedProperty: Any = "Hidden treasure"
val isItNumber = typeErasedProperty as? Int
val isItString = typeErasedProperty as? String