While Kotlin is not a pure functional language, it provides Higher order functions and an expressive way of writing programs vs statements provided by imperative languages like Java.
Function declaration
fun <A> asString(v:A): String = v.toString
val times2: (Int) -> Int = { n -> n * 2 }
val sum: (Int, Int) -> Int = { x, y -> x + y }A function can be expressed either as <I> functionName(arg: I): O prefixed by the word func or as a val defined with a type defining a function from I to O (I) -> O
Multiple input values can be declared as (A, B) -> C.
fun intToString(n:Int) = asString(n)
val times3 = { n:Int -> n * 3 }Functions can infer the return type
Function usage
val s = asString(8) // s = 8
val n = times2(2)// n = 4
val m = sum(1,2) // m = 3 val xs = listOf(1,2,3,4,5)
val ys = xs.map{ x -> x * 2 }.filter{ it < 5 }.map{ intToString(it) }In Kotlin, you cam use both a -> b op c notation or you can just refer to the element as it, similar to this within a context of a class
fun Int.pow(exp:Int):Double = Math.pow(this.toDouble(), exp.toDouble())
val r = 2.pow(3) // r = 8.0Simple extension method pow(exp: Int) for Int.
fun Int.square():Double = this.pow(2)
val r = 2.square() // r = 4.0Extension method square() uses pow(exp: Int) under the hood.
Algebraic data types (ADT)
abstract class Tree<T>
data class Branch<T>(val left: Tree<T>, val value: T, val right: Tree<T>): Tree<T>()
data class Leaf<T>(val value: T): Tree<T>() fun <T,R> Tree<T>.map(f: (T) -> R): Tree<R> {
return when(this) {
is Branch -> Branch(this.left.map(f),f(this.value), this.right.map(f))
is Leaf -> Leaf(f(this.value))
else -> throw Exception("KABOOM!!!")
}
}
Leaf(2).map{ it * it } // Leaf(value=4)<T,R> Tree<T>.map(f: (T) -> R) uses pattern matching to decide what to do.
- If
thisis an instance ofBranch, it will apply map to both sides of the tree and it will apply the functionfto the value present in that node. - If
thisis aLeaf, it will apply the functionftovalue. - Unfortunately, Kotlin is not smart enough to tell that we have implemented all the possible scenarios, so we need to add the
elsepart of the Pattern Matching throwing an exception that, we know, will never happen.
Appart from being an example for Pattern Matching, <T,R> Tree<T>.map(f: (T) -> R) is an extension method for our ADT Tree<T> and it is also using [recursion][1] to apply the function f through the tree structure.
Finally <T,R> Tree<T>.map(f: (T) -> R) is a Higher Order Function since it receives a function f: (T)-> R as its argument.