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.0
Simple extension method pow(exp: Int)
for Int
.
fun Int.square():Double = this.pow(2)
val r = 2.square() // r = 4.0
Extension 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
this
is an instance ofBranch
, it will apply map to both sides of the tree and it will apply the functionf
to the value present in that node. - If
this
is aLeaf
, it will apply the functionf
tovalue
. - Unfortunately, Kotlin is not smart enough to tell that we have implemented all the possible scenarios, so we need to add the
else
part 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.