Functional Programming in Android

Teagan Glenn

      Teagan42

Senior Software Engineer

Aetna

Functional Programming

What is it?

Side-effect Free Coding

  • No reliance on data outside function
    • Operates only on the arguments passed in
  • Does not change data outside of function
    • Data passed in is not mutated
    • Data returned is always a new instance

Some Definitions

  • Pure Function - Given the same input, will always return the same output and produce no side-effects
  • First Class Functions - Functions are treated no different than variables; can be passed as arguments, returned from other functions, etc.
  • Higher Order Function - A function that accepts a function as an argument and/or a function that returns another function 
  • Map - Applies a given function to all elements of a collection, returning a list of the results
  • Reduce/Fold - Acts as an accumulator for a collection; example: sum of a collection of integers
  • Curry - A function that accepts fewer arguments than expected, returning a function that accepts the remaining arguments

Kotlin

The Basics

Variables & Fields

  • val - A variable that can't change during runtime (i.e. Java final)
  • var - A variable that may change during runtime
  • lateinit - A variable that is not initialized inline, but will be assigned before use, the type cannot be Nullable
  • const val - Compile time constant

Constructors

  • Primary Constructor
    • Declared inline with class header
    • Class fields are defined using var or val
  • Secondary Constructor
    • Declared in the class body
    • Class fields must be defined in class body
  • Initialization Block
    • Declared in the class body
    • Used to perform logic on object construction

Null

  • Only data types with a ? suffix can be nullable
    • Nullable String: var myString: String?
    • Non-nullable String: var myString: String
  • Use the Safe Call Operator (?.) to perform null checks on function and property chains
    • If any part of the chain is null, the result of the operation is null
  • Elvis Operator (?:) coalesces to the right operand if the left operand is null

Lambda

  • Is not declared, but is passed immediately as an expression
  • Should be expressive, not narrative
    • Atomic (Single Purpose)
    • Short and Sweet
  • Should be pure

Kotlin 

Standard Library

let

  • Transforms
  • Passes operant as it
  • Performs an expression block
  • Returns the result of the last item of the expression block

apply

  • Doesn't transform
  • Passes operant as this
  • Performs an expression block
  • Returns the instance that was passed in

Examples

/*
 * Kotlin let
 */
fun setActivityTitle(title: String?): Fragment? = 
    title?.let { 
        activity?.title = it
        this
    }

// With parameter name override
fun setActivityTitle(title: String?): Fragment? =
        title?.let { newTitle ->
            {
                activity?.title = newTitle
                this
            }() // Since it's a block, we need to evaluate it
        }

/*
 * Kotlin apply
 */
fun setActivityTitle(title: String?) = title?.apply { activity?.title = this }

run

  • Transforms
  • Passes operant as this
  • Performs an expression block
  • Returns the result of the last item of the expression block

also

  • Doesn't transform
  • Passes operant as this
  • Performs an expression block
  • Returns the instance that was passed in

Examples

/*
 * Kotlin run
 */
fun setActivityTitle(title: String?) = title?.run { activity?.title = this }

/*
 * Kotlin also
 */

fun setActivityTitle(title: String?): String? =
        title?.also { activity?.title = it }

// With parameter name override
fun setActivityTitle(title: String?): String? =
        title?.also { newTitle -> activity?.title = newTitle }


with

Syntactic sugar for run without an operant

use

Safely closes the operant upon completion of the block expression, regardless of exception

  • InputStreams
  • OutputStreams
  • Readers

Examples

/*
 * Kotlin with
 */
override fun onViewCreated() {
    with(my_text_view) {
        text = "My Default Text"
        setOnClickListener = textClickListener
        setOn
    }
}

/*
 * Kotlin use
 */
fun getTestData(context: Context, gson: Gson, jsonAssetName: String) =
    context.resources.assets.open().use {
        gson.fromJson(InputStreamConverter.getInputStreamAsString(this), SomeDataType::class.java)
    }

Kotlin

Extension Functions

infix

  • Allows invocation without . and ()
  • Can only accept one parameter
  • Can be thought of as operators

inline

  • When using higher order functions, each function is an object, with reference to each argument, held in memory
  • Inline functions do not instantiate each function, instead copying the code outside of the block invocation, reducing memory usage but increasing bytecode size

Examples

infix fun Int.mod(divisor: Int): Int = this % divisor

fun calculateChange(amount: Int) = amount mod 100

//--------------------\\

inline fun FragmentManager.inTransaction(func: FragmentTransaction.() -> Unit) {
    val fragmentTransaction = beginTransaction()
    fragmentTransaction.func()
    fragmentTransaction.commit()
}

fun setFragment(fragment: Fragment) {
    supportFragmentManager.inTransaction {
        replace(contentViewId, fragment, fragment::class.simpleName)
        addToBackStack.ifTrue { addToBackStack(fragment::class.simpleName) }
    }
}

//--------------------\\

fun String?.toUri(): Uri? =
    if (this == null) null
    else try {
        Uri.parse(this)
    } catch (e: Exception) {
        Timber.e(e)
        null
    }

fun goToUrl(address: String?) = startActivity(Intent(Intent.ACTION_VIEW, address?.toUri()))

Kotlin

Things to Remember

Adds to Compilation Time

Can Increase APK Size

Can Increase Memory Footprint

If We Use Kotlin

Use Functional Programming

Further Reading

Android Functional Programming with Kotlin

By Teagan Glenn

Android Functional Programming with Kotlin

Functional Programming Principles, Basics of Kotlin, Built-in Lambdas (let, apply, run, also, with), Extension Functions

  • 1,583