Skip to content
This repository has been archived by the owner on Dec 21, 2023. It is now read-only.

Using scope functions apply/with/run/also/let #35

Open
cypressious opened this issue Feb 20, 2017 · 7 comments
Open

Using scope functions apply/with/run/also/let #35

cypressious opened this issue Feb 20, 2017 · 7 comments

Comments

@cypressious
Copy link

cypressious commented Feb 20, 2017

apply

Use apply for initialization:

val foo = createBar().apply {
    property = value
    init()
}

also

Use also over apply if the receiver is used for anything other than setting properties or function calls on it:

class Baz {
    var currentBar: Bar?
    val observable: Observable

    val foo = createBar().also {
        currentBar = it
        observable.registerCallback(it)
    }
}

Prefer also over apply if there already are multiple receivers in scope, especially if you make calls on any outer receivers:

class Foo {
    fun Bar.baz() {
        val stuff = callSomething().also {
            it.init()
            this@baz.registerCallback(it)
        }
    }
}

apply/with/run

Prefer apply/run over with if the receiver is nullable.

getNullable()?.run {
    init()
}

getNullable()?.apply {
    init()
}

Prefer run/with over apply if the returned value is not used

view.run {
    textView.text = "Hello World"
    progressBar.init()
}

with(view) {
    textView.text = "Hello World"
    progressBar.init()
}

Choose one of the above and use it consistently.

let

Prefer let over run in method chains that transform the receiver

val baz: Baz = foo.let { createBar(it) }.convertBarToBaz()
// or with function references
val baz: Baz = foo.let(::createBar).convertBarToBaz()
@voddan
Copy link

voddan commented Feb 22, 2017

I use with over run if the object in question is a local variable or a short expression:

with(view) {
    textView.text = "Hello World"
    progressBar.init()
}

On the other hand, I use run in the end of a long call chain:

MyApp.defaultActivity.getActiveView().view.run {
    textView.text = "Hello World"
    progressBar.init()
}

@MarcinMoskala
Copy link

MarcinMoskala commented Jul 25, 2017

@cypressious Why you prefer run over apply if the returned value is not used? run is also returning value - the result of function literal. The same with with, but for this one, I also feel intuitively that it should be used this way.

@MarcinMoskala
Copy link

MarcinMoskala commented Jul 25, 2017

I use let when I just need to unpack nullable read-write property (when smart-casting is not working):

var text: String? = null
fun showText() {
    text?.let { text -> 
        println(text) 
    }
}

I often see let used this way.

@cypressious
Copy link
Author

@MarcinMoskala it's just to differentiate. I use apply when I need to the receiver as the return value, otherwise I don't use it.

@kingsleyadio
Copy link

Another good use of with is for executing a lambda in the context of a casted object:

with(activity as MyActivity) {
  myActivitymethod()
  ...
}

@tlazenka
Copy link

Using also for a "singleton with parameter" (from Google's architecture components sample code):

class UsersDatabase : RoomDatabase() {

    companion object {

        @Volatile private var INSTANCE: UsersDatabase? = null

        fun getInstance(context: Context): UsersDatabase =
                INSTANCE ?: synchronized(this) {
                    INSTANCE ?: buildDatabase(context).also { INSTANCE = it }
                }

        private fun buildDatabase(context: Context) =
                Room.databaseBuilder(context.applicationContext,
                        UsersDatabase::class.java, "Sample.db")
                        .build()
    }
}

https://github.com/googlesamples/android-architecture-components/blob/master/BasicRxJavaSampleKotlin/app/src/main/java/com/example/android/observability/persistence/UsersDatabase.kt

@zeljkot
Copy link

zeljkot commented Dec 9, 2021

I was trying out what to use when in the same year when this issue was created. My understanding back then is that these functions should be used based on intention:

  • apply: post-construction configuration
  • with: configure object created somewhere else
  • also: additional processing on an object in a call chain
  • let: conversion of value
  • run: execute lambda with side-effects and no result

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants