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

Andre Perkins
ProAndroidDev
Published in
3 min readJan 31, 2018

--

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

--

--

Android Developer, Television Enthusiast, Bad Joke Connoisseur | @Muru | New York, NY