Introduction to Functional Programming

Victor J. Reventos

What is Functional Programming?

  • It’s a style of programming that treats programs as evaluation of mathematical functions and avoids mutable state and side effects

Functional Programming Benefits

  • Declarative Code (what vs how)
  • Explicitness
  • Concurrency
  • Composability & Modularity
  • Testability
  • Easy to understand code

FP Concepts

  • Declarative vs Imperative
  • Pure Functions
  • Higher Order Functions
  • Function Composition
  • Immutability
  • Map/Filter/Reduce

Declarative vs Imperative

  • Defining WHAT to do vs HOW to do it
  • Expressive
  • Eliminate side-effects as much as possible

Declarative vs Imperative

// Imperative
fun evenNumbers(integers: List<Int>): List<Int> {
    val result = mutableListOf<Int>()

    for (i in integers) {
        if (i % 2 == 0) {
            result.add(i)
        }
    }

    return result
}

// Declarative / Function
fun evenNumbers(integers: List<Int>): List<Int> {
    // return integers.filter { i -> i % 2 == 0 }
    return integers.filter { it % 2 == 0 }
}

Pure Functions

  • Pure functions act on their parameters
  • Always produce the same output for the given parameters
    • Calling the function a "thousand times" should return the same value a every time.
  • Have No side effects

Pure Functions

fun value(): Int {
    return 10
}

fun add(x: Int, y: Int): Int = x + y

fun countCharacters(strings: List<String>): Int = strings
        .map(String::length)
        .sum()

fun countOccurences(names: List<String>): Map<String, Int> = names
        .groupingBy { it }
        .eachCount()

Impure Functions

class Impure {

    private var counter = 1

    fun value(): Int {
        return counter++
    }

    fun add(x: Int, y: Int): Int {
        return x + y + counter++
    }
}

// Another example
fun sayHello(person: Person): String {
    // Nasty side effect
    person.setGreeted(true)
    return "Hello " + person.getName()
}

Referential Transparency

  • An expression is said to be referentially transparent if it can be replaced with its corresponding value without changing the program's behavior
  • A function composed of pure functions is a referentially transparent function

 

In short, it does what the type signature says its going to do.

Referential Transparency

// referentially transparent
// no surprises
fun multiply(x: Int, y: Int): Int = x * y

// Not referentially transparent

// is not valid for y = 0 and it will throw an exception
fun divide(x: Int, y: Int): Int {
    return x / y
}

// Where does it force me to handle an error?
fun doSomething(): Int {
    // do work
    // sneaky who knew I would give you a nasty side effect
    // exceptions: the glorified GOTO statement.
    throw RuntimeException()
}

Higher Order Functions

  • Functions are first class citizens
    • Functions can take functions as arguments
    • Functions can return functions
  • Enables function composition

Higher Order Functions - Locking

fun <T> lock(lock: Lock, block: () -> T): T {
    lock.lock()
    try {
        return block()
    } finally {
        lock.unlock()
    }
}

// usage
val result = lock(someLock) {
    // use the shared resource here safely
}

Higher Order Functions - Timing

fun <T> time(block: () -> T, timeConsumer: (Long)-> Unit): T {
    val startTime = System.nanoTime()
    val value = block()
    timeConsumer(System.nanoTime() - startTime)
    return value
}

@Log4j2
class Timing {


    fun doRealWork(): Int {
        return 20 * 20
    }

    fun timeDoRealWork(): Int {
        return time(this::doRealWork, { log.info("doRealWork took $it ns") })
    }
}

Function Composition

 Is applying one function to the results of another: The result of f() is sent through g() It is written: (g º f)(x)

 

newFunction(x) = g(f(x))

Function Composition

import org.funktionale.composition.*

fun times2(x: Int) = x * 2

fun square(x: Int) = x * x

fun squareTimes2(x: Int) = (::square andThen ::times2)(x)

fun squareTimes4(x: Int) = (::square andThen ::times2 andThen ::times2)(x)

// see... LEGOS!
fun squareTimes2InHex(x: Int) = (::square andThen ::times2 andThen Integer::toHexString)

Immutability

  • An immutable object is an object whose state cannot be modified after it is created
    • Are thread-safe
    • Easy to test
    • Easy to track (no need to keep track how/where your object changes)
    • Favors caching

Immutability

data class ImmutableDataClass(val name: String, val phone: String)



data class MutableDataClass(val name: String, var phone: String)

Common Functional Functions

  • map - Transforms a value into another
  • filter - Returns the value if it matches the given predicate
  • reduce/fold - Applies a given function against an accumulator to reduce it to a single value

Problem - Count the number of Album titles that start with "Pop"

data class Album(val title: String)

fun countTheNumberOfTitlesThatStartWithPop(albums: List<Album>): Long {
    return albums
            // transform Album -> String
            .map(Album::title)
            // filter - keep titles that start with "Pop"
            .filter { it.startsWith("Pop") }
            // fold - Accumulates value starting with [initial] value 
            // and applying [operation] from left to right to current 
            // accumulator value and each element.
            // this could also be written using .count()
            .fold(0) { acc: Int, s: String ->  acc + 1}
            .toLong()
}

Advanced Topics

  • Monoids / Functors / Applicative
  • Functional Architecture
  • Functional Error Handling

Questions?

May the Function be with you.

Introduction to Functional Programming with Kotlin

By Victor J. Reventos

Introduction to Functional Programming with Kotlin

Introduction to functional programming with Kotlin examples.

  • 113