Photo by Charlota Blunarova on Unsplash

Functional Programming with Kotlin

Posted: 28 Nov 2017. Last modified on 04-Jun-22.

This article will take about 8 minutes to read.


Functional programming paradigms can reduce the complexity and risk of new code. It contrasts with Object oriented programming, but before we can look at functional programming, we need to go over some definitions.

What is Object Oriented Programming?

Object-Oriented Programming (OOP) refers to a type of computer programming in which programmers define not only the data type of a data structure, but also the types of operations that can be applied to the data structure.

This bundles together both the data and the cuntions whcare are acting upon that data. It cares about what an Object is, what state it is in, and what it can do.

What is Functional Programming?

Functional programming (FP) is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids changing state and mutable data. It is the process of building software by

Functional programming is declarative rather than imperative, and application state flows through pure functions.

When to choose OOP over FP

Object oriented languages are good when

Functional languages are good when

Adding new code

The type of code that you are adding will affect which paradigm you adhere to most closely. When adding new objects:

When adding new functionality:

It’s worth noting that there are fewer antipatterns reported for the functional paradigm. This is in large part due to its focus on immutability and statelessness.


Functional Programming concepts

Immutability

One of the core concepts of FP is immutability

Kotlin defines mutable and immutable types for many collections data structures, such as

The default in these cases is to use the immutable type.

When doing java interop, we treat ArrayLists as MutableLists.

Note that operations which are done on a mutably typed object which has been cast to an immutable object will be able to modify it.

val numbers: MutableList<Int> = mutableListOf(1, 2, 3)
val readOnlyView: List<Int> = numbers

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

numbers.add(4)

println(readOnlyView) // [1, 2, 3, 4]

readOnlyView.clear() // ERROR! We can't modify a read-only list.

Domain Modelling

Domain Driven Design allows for the representation of a Sealed classes are used for representing restricted class hierarchies, when a value can have one of the types from a limited set, but cannot have any other type. They are abstract and can only have private constructors.

Their set of values is restricted like an enum, but

sealed class Expr
data class Const(val number: Double): Expr()
data class Sum(val e1: Expr, val e2: Expr): Expr()
object NotANumber: Expr

fun eval(expr: Expr): Double = when(expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN // "is" is not required because we are checking if the instances are the same, instead of if their types match.
    // the else clause is not required because we have covered all of the cases.
}

Laziness

Delegated properties:

Using delegates, which are created using the by keyword, we can lazily insert the value that we need, only when it is accessed.

This reduces the amount of time that it takes to create new objects.

Dependency injection can be achieved via these delegated properties and inlining functions. One example of this in practice is the Koint DI framework.

class ConfigDelegate<R, C>(
    private val retainedState: WithConfig<C>?,
    private val defaultConfig: C
): ReadWriteProperty<R, C> {
    private var _config: C? = null

    override fun getValue(thisRef: R, property: KProperty<*>): C = _config
        ?.also { retainedState?.config = it }
        ?: retainedState.config
        ?: defaultConfig

    override fun setValue(thisRef: R, property: Kproperty<*>, value: C){
        _config = value
    }

    interface WithConfig<C>{
        var config: C
    }
}
class WeatherActivity(): AppCompatActivity() {
    val presenter by inject<Weatherpresenter>
}

class WeatherModule: AndroidModule() {
    override fun context() = applicationContext {
        context(name = "WeatherActivity") {
            provide { WeatherPresenter(get()) }
        }
        provide { WeatherRepository(get()) }
        provide { LocalDataSource(AndroidReader(applicationContext)) } bind WeatherDataSource::class
    }
}
val lazyValue : String by lazy {
    println("Computed")
    "Hello"
}

fun main(args:Array<String>){
    println(lazyValue)
    println(lazyValue) // the second call will return the previously computed value
}

Are there any negatives to using functional programming?

Overhead of new functions

Every time we use higher order functions, we are creating a new Function object. This means that the functional code will have higher memory utilization, and slower compute speeds.

Solution: Inline Functions

Marking a function as inline will allow us to reduce overhead of these functional techniques to nearly zero. However, we have to remember to use them and we need to understand when inlining is appropriate.

inline fun <T> lock(lock: Lock, body: () -> T): T {
    lock.lock()
    try {
        body()
    }
    finally {
        lock.unlock()
    }
}

would become the following after compilation:

lock(l) { foo() }

// Contents of the "body" lambda

l.lock()
try {
foo()
} finally {
l.unlock()
}

Reification incompatibility

Reification is when we make an inline function work with “Real” types

treeNode.findParentOftype(MyTreeNode::class.java)
//...
treeNode.findParentOftype<MyTreeNode>()

Reification is not compatible with Java, because we would be referring to a generic type which has already been erased.

Unenforced Purity

Since kotlin still allows for OOP, any method call can still be potentially side effecting. There are no const functions yet.

Immutable types can be changed if there is still a mutable instance of the variable.

The kotlin standard library does not have a high-performance immutable collections library yet (as scala does)

See these articles for more information