Functional Patterns

in Kotlin

Amber Doctor

May 25, 2018

Config Example: Class

class EmailSender (private val ApiKey: String,
                   private val useSandbox: Boolean) {
    fun sendEmail(emailContent: EC): String {
        val emailer = Emailer(ApiKey)
        emailer.addRequestHeader(
                useSandbox,
                "Bearer $ApiKey")
        val request = buildEmail(emailContent)
        println("Sent ${request.subjectText}")
        return emailer.api(request)
    }
}

Config Example: Function

fun getEmailSender(ApiKey: String, useSandbox: Boolean):
        (emailContent: EC) -> String {
    return {
        emailContent: EC ->
        val emailer = Emailer(ApiKey)
        emailer.addRequestHeader(
                useSandbox,
                "Bearer $ApiKey")
        val request = buildEmail(emailContent)
        println("Sent ${request.subjectText}")
        emailer.api(request)
    }
}

Config Ex: Compare Invocation

fun main(args: Array<String>){
    // some data - same for both
    val myEmailContent = EC("First Email", "World", "Hello")
    val myEmailContent2 = myEmailContent.copy(subjectText = "Second Email")

    // with functions
    // You can do this 1 time in the whole project
    val myEmailSender = getEmailSender("ValidApiKey", true)

    myEmailSender(myEmailContent) // Sent First Email
    myEmailSender(myEmailContent2) // Sent Second Email


    // with class
    val myClassEmailSender = EmailSender("ValidApiKey", true)

    myClassEmailSender.sendEmail(myEmailContent) // Sent First Email
    myClassEmailSender.sendEmail(myEmailContent2) // Sent Second Email
}

1 Time Config: Class

class EmailSenderOT (private val ApiKey: String,
                     private val useSandbox: Boolean) {
    private val emailer = Emailer(ApiKey)
    init {
        emailer.addRequestHeader(
                useSandbox,
                "Bearer $ApiKey")
    }
    fun sendEmail(emailContent: EC): String {
        val request = buildEmail(emailContent)
        println("Sent ${request.subjectText}")
        return emailer.api(request)
    }
}

1 Time Config: Function

fun getEmailSenderOT(ApiKey: String, useSandbox: Boolean):
        (emailContent: EC) -> String {
    val emailer = Emailer(ApiKey)
    emailer.addRequestHeader(
            useSandbox,
            "Bearer $ApiKey")
    return {
        emailContent: EC ->
        val request = buildEmail(emailContent)
        println("Sent ${request.subjectText}")
        emailer.api(request)
    }
}

Functional Advantage?

class EmailSenderOT (private val ApiKey: String,
                  private val useSandbox: Boolean) {
    private val emailer = Emailer(ApiKey)
    init {
        emailer.addRequestHeader(
                useSandbox,
                "Bearer $ApiKey")
    }
    fun sendEmail(emailContent: EC): String {
        // uses ApiKey & useSandbox
    }
    fun buildEmail(emailContent: EC): EC {
        // uses none of the constructor params
        // do stuff
        return emailContent
    }
}

Functional Advantage

fun buildEmail(emailContent: EC): EC {
    // uses none of the constructor params
    // do stuff
    return emailContent
}

Easier testing - don't have to create:

  • class EmailSender
  • params for email sender
    • (remember buildEmail didn't need them)

Use buildEmail w/out class overhead (same testing bullets)

Variables in a closure have perfect encapsulation.  

Side effects are more explicit.

 

State Example: Class

class Counter(private var currentNumber: Int) {
    init {
        currentNumber -= 2
    }
    fun countByTwo(): Int {
        currentNumber += 2
        return currentNumber
    }
}


fun main(args: Array<String>){
    val myClassCounter = Counter(0)
    println(myClassCounter.countByTwo()) // 2
    println(myClassCounter.countByTwo()) // 4
    println(myClassCounter.countByTwo()) // 6
    println(myClassCounter.countByTwo()) // 8
}

State Example: Function

fun getCountByTwo(startNumber: Int): ()-> Int {
    var currentNumber = startNumber - 2
    return {
        currentNumber += 2
        currentNumber
    }
}


fun main(args: Array<String>){
    val myCounter = getCountByTwo(0)
    println(myCounter()) // 2
    println(myCounter()) // 4
    println(myCounter()) // 6
    println(myCounter()) // 8
}

Thanks

For full code examples:

https://github.com/amberdoctor/functional-patterns

 

For questions:

amberdoctor+functional.patterns.demo@gmail.com

Functional Patterns

By Amber Doctor

Functional Patterns

  • 1,185