Slide 1

Slide 1 text

Functional programming in Kotlin with funKTionale Mario Arias - f(by) 2016 @dh44t

Slide 2

Slide 2 text

Topics About me A tiny introduction to Kotlin Function types Extension points Function composition, currying and partial application funKTionale’s Option, Either and Disjunction Future developments

Slide 3

Slide 3 text

Software Engineer at Cake Solutions 10+ years of experience with JVM technologies Spring certified trainer 5+ years with Scala 3+ years with Kotlin funKTionale KotlinPrimavera RxKotlin original developer and former team leader NOT an expert on functional programming

Slide 4

Slide 4 text

What is Kotlin? Statically typed programming language for the JVM, Android and the browser* *LLVM and iOS coming in the future

Slide 5

Slide 5 text

Hello world fun main(args: Array) {
 println("Hello Minsk!")
 }

Slide 6

Slide 6 text

Features •Classes •sealed (ADT), data •Interfaces with body methods •Objects •Companion objects •Enum •Generics •Named and default parameters

Slide 7

Slide 7 text

Features II •Control structures •if else, when •do, while, for •Expressions •Null Safety •High order functions •Delegates •Extension functions

Slide 8

Slide 8 text

Null Safety val hello: String = “" 
 val hello: String = null //compiler error 
 val hello: String? = null 
 val reversed: String? = hello?.reversed() 
 val otherReversed: String = reversed !!.reversed() //NPE

Slide 9

Slide 9 text

Null Safety II Any String Nothing

Slide 10

Slide 10 text

Null Safety III Any String Nothing Any? String?

Slide 11

Slide 11 text

Functions /** A function that takes 1 argument. */
 public interface Function1 : Function {
 /** Invokes the function with the specified argument. */
 public operator fun invoke(p1: P1): R
 }

Slide 12

Slide 12 text

Functions II fun doWithTwoNumbers(a: Int, b: Int, f: (Int, Int) -> Int): Int {
 return f(a, b)
 }
 
 val x = doWithTwoNumbers(2, 3, { a, b -> a + b })
 doWithTwoNumbers(x, 3) { a, b -> a * b }

Slide 13

Slide 13 text

Functions III fun unless(conditional: Boolean, body: () -> Unit) {
 if (!conditional) {
 body()
 }
 }
 
 unless(age > 18) {
 //Do something awesome
 } Create your own control structures!!

Slide 14

Slide 14 text

Delegates package org.funktionale.partials
 
 class PartialFunction(val definetAt: (P1) -> Boolean, f: (P1) -> R) : Function1 by f {
 fun isDefinedAt(p1: P1) = this.definetAt(p1)
 }

Slide 15

Slide 15 text

Delegated property val lazyValue: String by lazy {
 println("computed!")
 "Hello"
 }
 
 fun main(args: Array) {
 println(lazyValue)
 println(lazyValue)
 
 } lazy is NOT a language feature but a library function

Slide 16

Slide 16 text

Delegated property class User {
 var name: String by Delegates.observable("") {
 prop, old, new ->
 println("$old -> $new")
 }
 }
 
 fun main(args: Array) {
 val user = User()
 user.name = "first"
 user.name = "second"
 }

Slide 17

Slide 17 text

Extension functions fun String.fromMinsk() = "$this, FROM Minsk"
 
 println("Hello world".fromMinsk())

Slide 18

Slide 18 text

Extension functions II operator fun String.invoke(suffix:String) = "$this$suffix"
 
 println("This isn't a function"(" !!"))

Slide 19

Slide 19 text

Extension functions III val myList = listOf(1, 2, 3, 4)
 .map(Int ::toString) //call to method reference
 .map { i -> i + i }
 .map(String ::toInt)
 .filter { it > 20 } // it is an implicit parameter
 
 println(myList)

Slide 20

Slide 20 text

Combine all the things!!! infix fun (() -> Unit).unless(condition: Boolean) {
 if (!condition) {
 this()
 }
 }
 
 
 { /*do some magic */ } unless (age < 18) one-line unless from Ruby!!

Slide 21

Slide 21 text

funKTionale Functional constructs and patterns for Kotlin Currently at 0.9.7 version

Slide 22

Slide 22 text

Function composition A technique to create a new function using two existing functions. % ps aux | grep java

Slide 23

Slide 23 text

infix fun Function1.andThen(f: (IP)  R): (P1)  R = forwardCompose(f)
 
 infix fun Function1.forwardCompose(f: (IP)  R): (P1)  R {
 return { p1: P1  f(this(p1)) }
 }
 
 infix fun Function1.compose(f: (P1)  IP): (P1)  R {
 return { p1: P1  this(f(p1)) }
 }


Slide 24

Slide 24 text

fun main(args: Array) {
 val conf = SparkConf().setMaster("local").setAppName("My App")
 val sc = JavaSparkContext(conf)
 val split: (String) -> List = { it.split("|") }
 val upper: (String) -> String = { it.toUpperCase() }
 val user: (List) -> 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) {
 val conf = SparkConf().setMaster("local").setAppName("My App")
 val sc = JavaSparkContext(conf)
 val split: (String) -> List = { it.split("|") }
 val upper: (String) -> String = { it.toUpperCase() }
 val user: (List) -> 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

Slide 25

Slide 25 text

Currying

Slide 26

Slide 26 text

http://singletrackworld.com/forum/topic/nice-simple-tasty-curry-recipe

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

fun Function2.curried(): (P1)  (P2)  R {
 return { p1: P1  { p2: P2  this(p1, p2) } }
 }
 
 
 fun Function3.curried(): (P1)  (P2)  (P3)  R {
 return { p1: P1  { p2: P2  { p3: P3  this(p1, p2, p3) } } }
 }
 //… all the way to Function22 


Slide 29

Slide 29 text

fun main(args: Array) {
 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) {
 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())
 }

Slide 30

Slide 30 text

Partial applied functions Calling a function with less parameters than the function’s arity (fixing parameters) will return a new function with a smaller arity f(x,y,z) f(1,2) => g(z)

Slide 31

Slide 31 text

fun Function2.partially1(p1: P1): (P2)  R {
 return { p2: P2  this(p1, p2) }
 }
 
 
 fun Function2.partially2(p2: P2): (P1)  R {
 return { p1: P1  this(p1, p2) }
 }
 
 
 fun Function3.partially1(p1: P1): (P2, P3)  R {
 return { p2: P2, p3: P3  this(p1, p2, p3) }
 }
 
 
 fun Function3.partially2(p2: P2): (P1, P3)  R {
 return { p1: P1, p3: P3  this(p1, p2, p3) }
 }
 
 
 fun Function3.partially3(p3: P3): (P1, P2)  R {
 return { p1: P1, p2: P2  this(p1, p2, p3) }
 } //All the way to Function22

Slide 32

Slide 32 text

fun Function2.invoke(p1: P1, partial2: Partial = partial()): (P2)  R {
 return { p2: P2  this(p1, p2) }
 }
 
 
 fun Function2.invoke(partial1: Partial = partial(), p2: P2): (P1)  R {
 return { p1: P1  this(p1, p2) }
 }
 
 
 fun Function3.invoke(p1: P1, partial2: Partial = partial(), partial3: Partial = partial()): (P2, P3)  R {
 return { p2: P2, p3: P3  this(p1, p2, p3) }
 }
 
 
 fun Function3.invoke(partial1: Partial = partial(), p2: P2, partial3: Partial = partial()): (P1, P3)  R {
 return { p1: P1, p3: P3  this(p1, p2, p3) }
 }
 
 
 fun Function3.invoke(partial1: Partial = partial(), partial2: Partial = partial(), p3: P3): (P1, P2)  R {
 return { p1: P1, p2: P2  this(p1, p2, p3) } } //All the way to Function22…. This file has 2532 lines 
 


Slide 33

Slide 33 text

dr_id dr_name dr_age 1 Carlos 37 2 Laura 28 3 Ari 25 doctors p_id p_name p_age 1 Andy 55 2 Riba 26 3 Joanie 33 5 Ben 32 6 Pete 45 patients n_id n_name n_age 1 Diana 33 2 Sarah 28 3 Liz 26 nurses

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Option Why do you want Option in a null-safe language ??

Slide 36

Slide 36 text

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)

Slide 37

Slide 37 text

Examples without Option Representation Example Problems A value of the same type defined 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

Slide 38

Slide 38 text

Option in a null-safe language //scala 2.11 val op1:Option[String] = null // :crying_cat_face:
 val op2:Option[String] = Some(null) // :crying_cat_face: //kotlin
 val op1: Option = null //compilation error
 val op2: Option = Some(null) // compilation error
 
 val op3: Option? = null // really ??
 val op4: Option = Some(null) // do you really want this?
 op4.map { s -> s !!.reversed() } val op5: Option = Some("funcBy")

Slide 39

Slide 39 text

fun divide(num: Int, den: Int): Int? {
 return if (num % den != 0) {
 null
 } else {
 num / den
 }
 }
 
 fun division(a: Int, b: Int, c: Int): Pair? {
 val ac = divide(a, c)
 return when (ac) {
 is Int -> {
 val bc = divide(b, c)
 when (bc) {
 is Int -> ac to bc
 else -> null
 }
 }
 else -> null
 }
 } 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)

Slide 40

Slide 40 text

fun divide(num: Int, den: Int): Option {
 return if (num % den != 0) {
 None
 } else {
 Some(num / den)
 }
 }
 
 fun division(a: Int, b: Int, c: Int): Option> {
 val ac = divide(a, c)
 return when (ac) {
 is Some -> {
 val bc = divide(b, c)
 when (bc) {
 is Some -> {
 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 Migrated to Option

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

Either An unbiased representation of two possibles return values (left or right), commonly used to represent an error or a successful operation

Slide 43

Slide 43 text

Either II data class User(val name: String)
 
 fun getUser(url: String): Either { //do some magic
 }
 
 val either = getUser("http: //myapi.com/user/1")
 when(either){
 is Left -> println(either.left().get())
 is Right -> println(either
 .right().map { it.copy(name = it.name.capitalize()) }
 .right().get())
 }

Slide 44

Slide 44 text

Either III data class User(val name: String)
 
 fun getUser(url: String): Either = eitherTry {
 // do some magic
 } 
 val either = getUser("http: //myapi.com/user/1")
 when(either){
 is Left -> println(either.left().get().message)
 is Right -> println(either
 .right().map { it.copy(name = it.name.capitalize()) }
 .right().get())
 }

Slide 45

Slide 45 text

Disjunction A right-biased Either

Slide 46

Slide 46 text

Disjunction II fun getUser(url: String): Disjunction = disjunctionTry {
 //do some magic
 }
 
 val disjunction = getUser("http: //myapi.com/user/1")
 when(disjunction){
 is Left -> println(disjunction.swap().get().message)
 is Right -> println(disjunction
 .map { it.copy(name = it.name.capitalize()) }
 .get())
 }

Slide 47

Slide 47 text

Disjunction III val user1 = getUser("http: //myapi.com/user/1")
 val user2 = getUser("http: //myapi.com/user/2")
 
 val userList: Disjunction> = user1.flatMap { u1 ->
 user2.map { u2 ->
 listOf(u1, u2)
 }
 }
 
 val userList2: Disjunction, List> = validate(user1,user2){ u1, u2 ->
 listOf(u1, u2)
 }

Slide 48

Slide 48 text

Future developments •Kotlin 1.1 •Type Alias •Coroutines •funKTionale 1.0 •Clean up •Modularisation* •funKTionale 1.1 + •More monads (State and others) * funKTionale-0.9.7.jar (1.3 MB) > kotlin-stdlib-1.0.5-2.jar (564k)

Slide 49

Slide 49 text

Thanks!! (PS: We’re hiring, contact me :) )