36

I've read the docs on it 3 times and I still have no idea what it does. Can someone ELI5 (Explain Like I'm Five) it please? Here's how I'm using it:

fun main(args: Array<String>) {
    val UserModel = UserModel()
    val app = Javalin.create().port(7000).start()

    with (app) {
        get("/users") {
            context -> context.json(UserModel)
        }
    }
}
Vlady Veselinov
  • 4,678
  • 5
  • 23
  • 49
  • 4
    Wow, your question made me realize there is no good documentation on what makes `with` - being a **function**, not a keyword - actually work! So I wrote a question and answered it at the same type, maybe it'll add some value to the already existing answers. See: [What is a “receiver” in Kotlin?](https://stackoverflow.com/q/45875491/7079453) – F. George Aug 25 '17 at 06:23

5 Answers5

67

with is used to access an object's members and methods without having to refer to the object once per access. It is (mostly) for abbreviating your code. It is frequently used when constructing an object:

// Verbose way, 204 characters:
var thing = Thingummy()
thing.component1 = something()
thing.component2 = somethingElse()
thing.component3 = constantValue
thing.component4 = foo()
thing.component5 = bar()
parent.children.add(thing)
thing.refcount = 1

// Terse way, 182 characters:
var thing = Thingummy()
with(thing) {
  component1 = something()
  component2 = somethingElse()
  component3 = constantValue
  component4 = foo()
  component5 = bar()
  parent.children.add(this)
  refcount = 1
}
Paul Hicks
  • 13,289
  • 5
  • 51
  • 78
  • 1
    This is also a great time to point out that this encourages immutability; the second `var thing` should become a `val thing` and guarantee it's never changed unless you want it to change – Ky - Nov 27 '17 at 01:46
  • 3
    Simple yet to to the point. I, personally, think answers like this deserve more visibility than verbose answers. As PO themselves explicitly asked for "Explain Like I'm Five". Can't undermine other answer in this thread, but can say one thing ability to simplify something means you've got the gist – Farid Aug 20 '19 at 11:43
  • 3
    @Supuhstar Immutability has nothing to do with it. `val` merely means, you can't reassign. It does not mean that you can't change state. If the second `thing`could be `val`, so could the first – Frank Neblung Jan 10 '20 at 07:55
  • That's also true, @FrankNeblung. I wish Kotlin had the same properties that Swift does with its `let` keyword on value types. – Ky - Jan 11 '20 at 04:22
  • 1
    I think this example does not return the actual `Thingummy` object from the `with` block. `this` should be the last line in the lambda. – moffeltje Jun 11 '20 at 11:49
  • Yep good catch. The [refdocs](https://kotlinlang.org/docs/reference/scope-functions.html#with) recommend not using the return from `with` at all, since it doesn't agree with the way we humans use the word. Updating example accordingly. – Paul Hicks Sep 10 '20 at 03:34
  • What is the main difference of `with` from scope functions? – Bitwise DEVS Sep 08 '21 at 02:41
25

The documentation says:

inline fun <T, R> with(receiver: T, block: T.() -> R): R (source)

Calls the specified function block with the given receiver as its receiver and returns its result.

The way I think of it is that it is calling a function (the block) where this in the scope of the block is the receiver. Whatever the block returns is the return type.

Essentially calling a method where you provide the implicit this and can return any result from it.

Here is an example to demonstrate:

val rec = "hello"
val returnedValue: Int = with(rec) {
  println("$this is ${length}")
  lastIndexOf("l")
}

The rec in this case is the receiver of the function call - the this in the scope of the block. The $length and lastIndexOf are both called on the receiver.

The return value can be seen to be an Int because that is the last method call in the body - that is the generic type parameter R of the signature.

mkobit
  • 43,979
  • 12
  • 156
  • 150
  • So it's just a tool for saying what scope you want to be in? – Vlady Veselinov Sep 18 '17 at 02:35
  • 1
    @VladyVeselinov essentially, yes. There are a few other scoping functions that can help you right clearer code. You can see some examples and discussion about general usage patterns in the [yole/kotlin-style-guide GitHub repository](https://github.com/yole/kotlin-style-guide/issues/35). – mkobit Sep 18 '17 at 12:15
7

The definition of with:

inline fun <T, R> with(receiver: T, block: T.() -> R): R (source)

Actually it's implementation is straight forward: The block is executed on receiver, which works for any type:

receiver.block() //that's the body of `with`

The great thing to mention here, is the parameter type T.() -> R: It's called function literal with receiver. It's actually a lambda that can access the receiver's members without any additional qualifiers.

In your example the context of with receiver app is accessed in that way.

Besides stdlib functions like with or apply, this functionality is what makes Kotlin great for writing Domain Specific Languages as it allows the creation of scopes within which you have access on certain functionalities.

s1m0nw1
  • 76,759
  • 17
  • 167
  • 196
2
val citizen2 = Citizen("Tom", 24, "Washington")

val age = with(citizen2) {
    println("$name -  $age  $residence ")
    age = this.age + age
    residence = "Florida"
    age+10 // returns 58
}
println("${citizen2.name} -  ${citizen2.age} - $age - ${citizen2.residence}  ")

data class Citizen(var name: String, var age: Int, var residence: String)

Output:

Tom -  24  Washington 
Tom -  48 - 58 - Florida

Note that :

  • We can access age property of citizen(receiver object) with this.age or age
  • last line(age+10 in this example) in the lambda of with() returns.
oiyio
  • 5,219
  • 4
  • 42
  • 54
0

With is used to apply several operations to an object or access object's methods e.g. in this example we are accessing String's capitalize() extension method

data class Person(val name:String)
fun main(){
   val person = Person("john doe")
    with(person) {
       println(name.capitalize()) // output John Doe
    }

}

Under the hood with is a higher-order function. Here we are saying with the Person name call capitalize ( ). We don’t actually need ‘this’ because it is implicit and can be removed

Tonnie
  • 4,865
  • 3
  • 34
  • 50