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
- Kotlin API Reference https://kotlinlang.org/docs/reference/
- Kotlin Standard Library Explained https://bloggie.io/@_junrong/how-to-use-kotlin-s-it-also-let-apply-run
- Kotlin Tutorials https://www.programiz.com/kotlin-programming
- Kotlin Forum https://discuss.kotlinlang.org/
- Kotlin Subreddit https://www.reddit.com/r/Kotlin/
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,706