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.
Made with Slides.com