Mario Arias
September 23, 2015
2.9k

# Functional programming in Kotlin with funKTionale

Function composition, curried functions, partial applied functions and Option type in Kotlin using funKTionale library

## Mario Arias

September 23, 2015

## Transcript

1. ### Functional programming in Kotlin with funKTionale Mario Arias - Kotlin

developers in Manchester

Option
3. ### Software Engineer at Cake Solutions 10+ years of experience with

JVM technologies Spring certiﬁed trainer 4+ years with Scala 2+ years with Kotlin funKTionale KotlinPrimavera RxKotlin original developer and team leader* NOT an expert on functional programming * I hit “Merge” and “Release” buttons
4. ### Introduction Concept Kotlin First class and higher-order functions Yes Pure

functions Yes* Recursion Yes Lazy evaluation Yes* Strong type system Yes*
5. ### Functions // explicit type val add2: (Int) -> Int =

{ i -> i + 2 } // inferred type  val add2 = { i:Int -> i + 2 }
6. ### Functional eye for the imperative guy fun factorial(n: Long): Long

{  var result = 1L  for (it in 1..n) {  result *= it  }  return result  } fun functionalFactorial(n: Long): Long {  fun go(n: Long, acc: Long): Long {  return if (n <= 0) {  acc  } else {  go(n - 1, n * acc)  }  }  return go(n, 1)  } fun tailrecFactorial(n: Long): Long {  tailrec fun go(n: Long, acc: Long): Long {  return if (n <= 0) {  acc  } else {  go(n - 1, n * acc)  }  }  return go(n, 1)  } factorial(20)* 0μs 0,025μs 0,05μs 0,075μs 0,1μs factorial functional tailrec Error Value 0,064 0,092 0,078 0,001 0,003 0,002 0,002 0,003 0,001 *Calculated with JMH, SampleTime mode
7. ### fun fib(n: Long): Long = when (n) {  0L ->

0  1L -> 1  else -> {  var a = 0L  var b = 1L  var c = 0L  for (it in 2..n) {  c = a + b  a = b  b = c  }  c  }  } fun functionalFib(n: Long): Long {  fun go(n: Long, prev: Long, cur: Long): Long {  return if (n == 0L) prev  else go(n - 1, cur, prev + cur)    }  return go(n, 0, 1)  } fun tailrecFib(n: Long): Long {  tailrec fun go(n: Long, prev: Long, cur: Long): Long {  return if (n == 0L) prev  else go(n - 1, cur, prev + cur)    }  return go(n, 0, 1)  } ﬁb(93)* 0μs 30000μs 60000μs 90000μs 120000μs ﬁb functional tailrec Error Value 110.028 115.192 97.997 0,003 0,012 0,013 0,013 0,012 0,003 *Calculated with JMH, SampleTime mode
8. ### Function composition A technique to create a new function using

two existing functions. % ps aux | grep java
9. ### fun main(args: Array<String>) {  val conf = SparkConf().setMaster("local").setAppName("My App")  val

sc = JavaSparkContext(conf)  val split: (String) -> List<String> = { it.split("|") }  val upper: (String) -> String = { it.toUpperCase() }  val user: (List<String>) -> User = { User(it[0], it[1].toInt()) }  val users = sc.textFile("s3://path/to/my-petabyte-file.txt")  .map(upper)  .map(split)  .map(user)    users.take(20).forEach { println(it) }  } import org.funktionale.composition.andThen fun main(args: Array<String>) {  val conf = SparkConf().setMaster("local").setAppName("My App")  val sc = JavaSparkContext(conf)  val split: (String) -> List<String> = { it.split("|") }  val upper: (String) -> String = { it.toUpperCase() }  val user: (List<String>) -> User = { User(it[0], it[1].toInt()) }  val users = sc.textFile("s3://path/to/my-petabyte-file.txt")  .map(upper andThen split andThen user)    users.take(20).forEach { println(it) }  } Each map() transformation could be potentially distributed across nodes/ partitions* Just one map() transformation composed by several functions * Yes, Apache Spark is compatible with Kotlin

12. ### Currying Transforming a function of arity n into a sequence

of n functions with arity 1 (x, y, z) => r (x) => (y) => (z) => r https://wiki.haskell.org/Haskell_Brooks_Curry
13. ### fun main(args: Array<String>) {  val conf = SparkConf().setMaster("local[*]").setAppName("ML")  val sc

= JavaSparkContext(conf)    val spam = sc.textFile("spam.txt")  val ham = sc.textFile("ham.txt")    val tf = HashingTF(10000)    val posExamples = ham.map { LabeledPoint(1.0, tf.transform(listOf(it.split(" ")))) }  val negExamples = spam.map { LabeledPoint(0.0, tf.transform(listOf(it.split(" ")))) }  val trainData = posExamples.union(negExamples)  trainData.cache()  val model = LogisticRegressionWithLBFGS().run(trainData.rdd())  } import org.funktionale.currying.curried fun main(args: Array<String>) {  val conf = SparkConf().setMaster("local[*]").setAppName("ML")  val sc = JavaSparkContext(conf)    val spam = sc.textFile("spam.txt")  val ham = sc.textFile("ham.txt")    val tf = HashingTF(10000)    val labeling = { label: Double, email: String ->  LabeledPoint(label, tf.transform(listOf(email.split(" "))))  }  val curried = labeling.curried()  val posExamples = ham.map(curried(1.0))  val negExamples = spam.map(curried(0.0))  val trainData = posExamples.union(negExamples)  trainData.cache()  val model = LogisticRegressionWithLBFGS().run(trainData.rdd())  }
14. ### Partial applied functions Calling a function with less parameters than

the function’s arity (ﬁxing parameters) will return a new function with a smaller arity f(x,y,z) f(1,2) => g(z)
15. ### import org.kotlinprimavera.jdbc.core.extract val doctors = template.query("select * from doctors") {

rs, i ->  rs.extract { //DSL from KotlinPrimavera  User(string["dr_name"]!!, int["dr_age"]!!)  }  }    val nurses = template.query("select * from nurses") { rs, i ->  rs.extract {  User(string["n_name"]!!, int["n_age"]!!)  }  }    val patients = template.query("select * from patients") { rs, i ->  rs.extract {  User(string["p_name"]!!, int["p_age"]!!)  }  } import org.kotlinprimavera.jdbc.core.extract import org.funktionale.partials.* val mapper: (ResultSet, Int, String) -> User = { rs, i, prefix ->  rs.extract { //DSL from KotlinPrimavera  User(string["\${prefix}_name"]!!, int["\${prefix}_age"]!!)  }  }    val doctors = template.query("select * from doctors", mapper(p3 = "dr"))    val nurses = template.query("select * from nurses", mapper.partially3("n"))    val patients = template.query("select * from patients", mapper(p3 = "p"))
16. ### Option “Don’t stain my null-safe language with your monads” -

No one, never* * For some deﬁnitions of “No one” and “never”
17. ### Option is a type that represent the existence or absence

of a meaningful value Examples of meaningful value • Succesful operation (no exceptions) • An existent value (record in DB) • An useful value (non-empty list)
18. ### Examples without Option Representation Example Problems A value of the

same type deﬁned by convention indexOf(x) will return -1 if x doesn’t exists in the structure (Array, List) • Is not mandatory to check • Based on oral tradition An exception Spring’s JdbcTemplate will throw an EmptyResultDAE if no record is available* • Runtime Exception • Exception-based logic null Hibernate will return null if no record is available • is null * I kinda like it
19. ### Option operations • map • fold • flatMap • filter

• filterNot • exists • forEach • get • getOrElse • orElse fun getSome(): Option<String> = "kotlin".toOption()    fun getNone(): Option<String> = null.toOption()    @Test fun option() {  val option = getSome()  when (option) {  is Some<String> -> assertEquals(option.get(), "kotlin")   is None -> fail()  }    val otherOption = getNone()  when (otherOption) {  is Some<String> -> fail()  is None -> assertEquals(otherOption, None)  }  }    @Test fun getOrElse() {  assertEquals(getSome().getOrElse { "java" }, "kotlin")  assertEquals(getNone().getOrElse { "java" }, "java")  }    @Test fun orNull() {  assertNotNull(getSome().orNull())  assertNull(getNone().orNull())  }    @Test fun map() {  assertEquals(getSome().map { it.toUpperCase() }.get(), "KOTLIN")  assertEquals(getNone().map { it.toUpperCase() }, None)  }
20. ### fun divide(num: Int, den: Int): Option<Int> {  return if (num

% den != 0) {  None  } else {  Some(num / den)  }  }    fun division(a: Int, b: Int, c: Int): Option<Pair<Int, Int>> {  val ac = divide(a, c)  return when (ac) {  is Some<Int> -> {  val bc = divide(b, c)  when (bc) {  is Some<Int> -> {  Some(ac.get() to bc.get())  }  else -> None  }  }  else -> None  }  } An example* Based on Ken Barclay’s* post http://kenbarclay.blogspot.co.uk/2014/02/kotlin-option-type-2.html division function let me check if two numbers (a,b) are divisible by a third one (c) Even if ugly, this is still possible with -1, exceptions or null
21. ### fun division(a: Int, b: Int, c: Int): Option<Pair<Int, Int>> {

return divide(a, c).flatMap { ac ->  divide(b, c).flatMap { bc ->  Some(ac to bc)  }  }  } division with flatMap fun division(a: Int, b: Int, c: Int): Option<Pair<Int, Int>> {  val ac = divide(a, c)  return when (ac) {  is Some<Int> -> {  val bc = divide(b, c)  when (bc) {  is Some<Int> -> {  Some(ac.get() to bc.get())  }  else -> None  }  }  else -> None  }  } = replaced by 1st flatMap = replaced by 2nd flatMap
22. ### fun division(a: Int, b: Int, c: Int): Option<Pair<Int, Int>> {

return divide(a, c).flatMap { ac ->  divide(b, c).flatMap { bc ->  Pair(ac, bc).toOption()  }  }  } Overloading division fun division(a: Int, b: Int, c: Int, d: Int): Option<Triple<Int, Int, Int>> {  return divide(a, d).flatMap { ad ->  divide(b, d).flatMap { bd ->  divide(c, d).flatMap { cd ->  Triple(ad, bd, cd).toOption()  }  }  }  }