Book Image

Functional Kotlin

Book Image

Functional Kotlin

Overview of this book

Functional programming makes your application faster, improves performance, and increases your productivity. Kotlin supports many of the popular and advanced functional features of functional languages. This book will cover the A-Z of functional programming in Kotlin. This book bridges the language gap for Kotlin developers by showing you how to create and consume functional constructs in Kotlin. We also bridge the domain gap by showing how functional constructs can be applied in business scenarios. We’ll take you through lambdas, pattern matching, immutability, and help you develop a deep understanding of the concepts and practices of functional programming. If you want learn to address problems using Recursion, Koltin has support for it as well. You’ll also learn how to use the funKtionale library to perform currying and lazy programming and more. Finally, you’ll learn functional design patterns and techniques that will make you a better programmer.By the end of the book, you will be more confident in your functional programming skills and will be able to apply them while programming in Kotlin.
Table of Contents (22 chapters)
Title Page
Copyright and Credits
Dedication
Packt Upsell
Contributors
Preface
Index

Nullable types


One of the main features of Kotlin is nullable types. Nullable types allow us to define if a value can contain or being null explicitly:

fun main(args: Array<String>) {
    val myBlueberryCupcake: Cupcake = null //Compilation error: Null can not be a value of a non-null type Cupcake
}

This isn't valid in Kotlin; the Cupcake type doesn't allow null values. To allow null values, myBlueberryCupcake must have a different type:

fun main(args: Array<String>) {
    val myBlueberryCupcake: Cupcake? = null
}

In essence, Cupcake is a non-null type and Cupcake? is a nullable type.

In the hierarchical structure, Cupcake is a subtype of Cupcake?. So, in any situation where Cupcake? is defined, Cupcake can be used, but not the other way around:

fun eat(cupcake: Cupcake?){
//  something happens here    
}

fun main(args: Array<String>) {
    val myAlmondCupcake = Cupcake.almond()

    eat(myAlmondCupcake)

    eat(null)
}

Kotlin's compiler makes a distinction between instances of nullable and non-null types.

Let's take these values, for example:

fun main(args: Array<String>) {
    val cupcake: Cupcake = Cupcake.almond()
    val nullabeCupcake: Cupcake? = Cupcake.almond()
}

Next, we will invoke the eat() method on both nullable and non-null types:

fun main(args: Array<String>) {
    val cupcake: Cupcake = Cupcake.almond()
    val nullableCupcake: Cupcake? = Cupcake.almond()

    cupcake.eat() // Happy days
    nullableCupcake.eat() //Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Cupcake?
}

Calling the eat() method on cupcake is easy as pie; calling eat() on nullableCupcake is a compilation error.

Why? For Kotlin, calling a method from a nullable value is dangerous, a potential NullPointerException (NPE from now on) could be thrown. So, to be safe, Kotlin marks this as a compilation error.

What happens if we really want to invoke a method or access a property from a nullable value?

Well, Kotlin provides you options to deal with nullable values, with a catch—all are explicit. In some sense, Kotlin is telling you, Show me that you know what you are doing.

Let's review some options (there are more options that we'll cover in the following chapters).

Checking for null

Check for null as a condition in the if block:

fun main(args: Array<String>) {
    val nullableCupcake: Cupcake? = Cupcake.almond()

    if (nullableCupcake != null) {
      nullableCupcake.eat()
    }
}

Kotlin will do a smart cast. Inside the if block, nullableCupcake is a Cupcake, not a Cupcake?; so, any method or property can be accessed.

Checking for non-null types

This is similar to the previous one, but it checks directly for the type:

if (nullableCupcake is Cupcake) {
  nullableCupcake.eat()
}

It also works with when:

when (nullableCupcake) {
  is Cupcake -> nullableCupcake.eat()
}

Both options, checking for null and non-null types, are a little bit verbose. Let's check other options.

Safe calls

Safe calls let you access methods and properties of nullable values if the value isn't null (under the hood, at the bytecode level, a safe call is transformed into if(x != null)):

nullableCupcake?.eat()

But, what if you use it in an expression?

val result: String? = nullableCupcake?.eat()

It will return null if our value is null, so result must have a String? type.

That opens up the chance to use safe calls on a chain, as follows:

val length: Int? = nullableCupcake?.eat()?.length

The Elvis (?:) operator

The Elvis operator (?:) returns an alternative value if a null value is used in an expression:

val result2: String = nullableCupcake?.eat() ?: ""

If nullabluCupcake?.eat() is null, the ?: operator will return the alternative value "".

Obviously, the Elvis operator can be used with a chain of safe calls:

val length2: Int = nullableCupcake?.eat()?.length ?: 0

The (!!) operator

Instead of a null value, the !! operator will throw an NPE:

val result: String = nullableCupcake!!.eat()

If you can deal with an NPE, the !! operator gives you a pretty convenient feature, a free smart cast:

val result: String = nullableCupcake!!.eat()

val length: Int = nullableCupcake.eat().length

If nullableCupcake!!.eat() doesn't throw an NPE, Kotlin will change its type from Cupcake? to Cupcake from the next line and onwards.