Skip to content

Instantly share code, notes, and snippets.

@nicocavallo
Created July 16, 2018 12:18
Show Gist options
  • Save nicocavallo/0c2b667425f7e4938a6377e37489f309 to your computer and use it in GitHub Desktop.
Save nicocavallo/0c2b667425f7e4938a6377e37489f309 to your computer and use it in GitHub Desktop.
functional programming in Kotlin

Background

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.

Functions

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

Higher Order Functions (map, filter, etc.)

 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

Extension Methods

 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.

Data Classes

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>()

Pattern matching

 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 of Branch, it will apply map to both sides of the tree and it will apply the function f to the value present in that node.
  • If this is a Leaf, it will apply the function f to value.
  • 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment