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

Objects


We already covered object expressions, but there is more on objects. Objects are natural singletons (by natural, I mean to come as language features and not as behavior pattern implementations, as in other languages). A singleton is a type that has just one and only one instance and every object in Kotlin is a singleton. That opens a lot of interesting patterns (and also some bad practices). Objects as singletons are useful for coordinating actions across the system, but can also be dangerous if they are used to keep global state.

Object expressions don't need to extend any type:

fun main(args: Array<String>) {
    val expression = object {
        val property = ""

        fun method(): Int {
            println("from an object expressions")
            return 42
        }
    }

    val i = "${expression.method()} ${expression.property}"
    println(i)
}

In this case, the expression value is an object that doesn't have any specific type. We can access its properties and functions.

There is one restriction—object expressions without type can be used only locally, inside a method, or privately, inside a class:

class Outer {
    val internal = object {
        val property = ""
    }
}

fun main(args: Array<String>) {
    val outer = Outer()

    println(outer.internal.property) // Compilation error: Unresolved reference: property
}

In this case, the property value can't be accessed.

Object declarations

An object can also have a name. This kind of object is called an object declaration:

object Oven {
  fun process(product: Bakeable) {
    println(product.bake())
  }
}

fun main(args: Array<String>) {
    val myAlmondCupcake = Cupcake("Almond")
    Oven.process(myAlmondCupcake)
}

Objects are singletons; you don't need to instantiate Oven to use it. Objects also can extend other types:

interface Oven {
  fun process(product: Bakeable)
}

object ElectricOven: Oven {
  override fun process(product: Bakeable) {
    println(product.bake())
  }
}

fun main(args: Array<String>) {
    val myAlmondCupcake = Cupcake("Almond")
    ElectricOven.process(myAlmondCupcake)
}

Companion objects

Objects declared inside a class/interface can be marked as companion objects. Observe the use of companion objects in the following code:

class Cupcake(flavour: String) : BakeryGood(flavour), Bakeable {
  override fun name(): String { 
    return "cupcake" 
  } 

  companion object { 
    fun almond(): Cupcake { 
      return Cupcake("almond") 
    } 

    fun cheese(): Cupcake { 
      return Cupcake("cheese") 
    } 
  } 
}

Now, methods inside the companion object can be used directly, using the class name without instantiating it:

fun main(args: Array<String>) {
    val myBlueberryCupcake: BakeryGood = Cupcake("Blueberry")
    val myAlmondCupcake = Cupcake.almond()
    val myCheeseCupcake = Cupcake.cheese()
    val myCaramelCupcake = Cupcake("Caramel")
}

Companion object's methods can't be used from instances:

fun main(args: Array<String>) {
    val myAlmondCupcake = Cupcake.almond()
    val myCheeseCupcake = myAlmondCupcake.cheese() //Compilation error: Unresolved reference: cheese
}

Companion objects can be used outside the class as values with the name Companion:

fun main(args: Array<String>) {
    val factory: Cupcake.Companion = Cupcake.Companion
}

Alternatively, a Companion object can have a name:

class Cupcake(flavour: String) : BakeryGood(flavour), Bakeable {
    override fun name(): String {
        return "cupcake"
    }

    companion object Factory {
        fun almond(): Cupcake {
            return Cupcake("almond")
        }

        fun cheese(): Cupcake {
            return Cupcake("cheese")
        }
    }
}

fun main(args: Array<String>) {
    val factory: Cupcake.Factory = Cupcake.Factory
}

They can also be used without a name, as shown in the following code:

fun main(args: Array<String>) {
    val factory: Cupcake.Factory = Cupcake
}

Don't be confused by this syntax. The Cupcake value without parenthesis is the companion object; Cupcake() is an instance.