the tldr; on Kotlin’s let, apply, also, with and run functions

let
definition
/*** Calls the specified function [block] with `this` value as its argument and returns its result.*/public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
tldr;
- called object passed in via argument
- return type is whatever the lambda returns
typical usages
- convert from one type to another
- handling Nullability
// using 'let' to convert from one type to anotherval answerToUniverse = strBuilder.let { it.append("Douglas Adams was right after all") it.append("Life, the Universe and Everything") 42}// using 'let' to only print when str is not nullstr?.let { print(it) }
apply
definition
/*** Calls the specified function [block] with `this` value as its receiver and returns `this` value.*/public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
tldr;
- functional literal with receiver
- called object is returned
typical usage
- to Initialize or configure an object
// old way of building an objectval andre = Person()andre.name = "andre"andre.company = "Viacom"andre.hobby = "losing in ping pong"// after applying 'apply' (pun very much intended)val andre = Person().apply { name = "Andre" company = "Viacom" hobby = "losing in ping pong"}
also
/*** Calls the specified function [block] with `this` value as its argument and returns `this` value.*/@SinceKotlin("1.1")public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
tldr;
- called object passed in via argument
- called object is returned
typical usage
- side effects in chains
// transforming data from api with intermediary variableval rawData = api.getData()Log.debug(rawData)rawData.map { /** other stuff */ }// use 'also' to stay in the method chainsapi.getData() .also { Log.debug(it) } .map { /** other stuff */ }
with
definition
/*** Calls the specified function [block] with the given [receiver] as its receiver and returns its result.*/public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
tldr;
- not an extension function
- the relevant object is passed in as an argument
- returns whatever the lambda returns
typical usage
- logically group calls on an object
// Every Android Developer ever after Wednesday May 17th 2017 messageBoard.init(“https://url.com”) messageBoard.login(token) messageBoard.post(“Kotlin’s a way of life bro")
// using 'with' to avoid repetitive references to identifierwith(messageBoard) { init(“https://url.com”) login(token) post(“Kotlin’s a way of life bro")}
run
definition
/*** Calls the specified function [block] with `this` value as its receiver and returns its result.*/public inline fun <T, R> T.run(block: T.() -> R): R = block()
tldr;
- lambda with a receiver
- return type is whatever the lambda returns
- there is also a non-extension version of this function
typical usage
- same as let except allows receiver access
This next code snippet contains references to events that occur in Game of Thrones Season 7. you’ve been warned! :)
// GoT developers after season 7aegonTargaryen = jonSnow.run { makeKingOfTheNorth() swearsFealtyTo(daenerysTargaryen) realIdentityRevealed(“Aegon Targaryen”)}
ultimately…
- the various functions contain overlapping use cases
- don’t feel obligated to use each function
- most importantly, decide on consistency amongst your project/team