Compare Ktor's `install(ContentNegotiation) { json() }` configuration DSL with a settings-object DSL like `Json { }`. How are their builder lambdas structured, and what does the configuration lambda's receiver type tell you?
answer
- Receiver type = the menu inside the braces
- Json { } returns immutable config you hold
- install(Plugin) { } configures+registers into pipeline (side effect)
- Settings DSL assigns props; plugin Config exposes verbs
- Both are Builder.() -> Unit via trailing lambda
basics
~20 sBoth use a trailing lambda over a settings object. Json { } configures a JsonBuilder; install(Plugin) { } configures that plugin's own Configuration class. The receiver type tells you which settings are available inside the block.
solid answer
~40 sBoth follow the receiver-lambda-over-settings idiom. `Json { ... }` takes a `JsonBuilder.() -> Unit`; inside, `this` is a JsonBuilder and you set its fields. `install(plugin) { ... }` is generic: `fun install(plugin: Plugin<..., Configuration, ...>, configure: Configuration.() -> Unit)`. The lambda's receiver is the **plugin's own Configuration type**, so for ContentNegotiation the receiver exposes registration functions like `json(...)`, `register(...)`. The receiver type is the contract: whatever members the Configuration class declares are the available DSL verbs in the block. The difference is mostly intent: `Json { }` produces a standalone immutable config object you hold; `install { }` registers and configures a plugin into a pipeline as a side effect, and the configured plugin lives inside the application rather than being returned to you. Both rely on the same function-type-with-receiver mechanism.
code
kotlin · 9 lines// settings DSL: returns an immutable value
val json: Json = Json { prettyPrint = true; ignoreUnknownKeys = true }
// plugin DSL: receiver is ContentNegotiationConfig; side effect installs into pipeline
fun Application.module() {
install(ContentNegotiation) {
json(json) // 'json' is a verb of the plugin's Configuration
}
}go deeper
Recognizes both are { } config blocks but may not name the receiver types.
Identifies the Configuration receiver as the contract and that install has a side effect vs Json returning a value.
Explains nesting, properties-vs-verbs, and how to introspect available DSL members via the Configuration type.
Reasons about plugin/extension API design: pipeline registration, discoverability, and composing standalone configs into plugins.
## Two flavors of the same idiom Both `Json { }` and `install(Plugin) { }` are **receiver-lambda config DSLs**. The lambda's **receiver type** determines what you can write inside the braces. ### Settings-object DSL: `Json { }` ```kotlin fun Json(builderAction: JsonBuilder.() -> Unit): Json ``` - Receiver: `JsonBuilder` — a mutable settings holder. - Inside: assign `var` properties (`isLenient`, `prettyPrint`, `ignoreUnknownKeys`). - Output: returns an **immutable `Json`** you keep and use. ### Plugin-config DSL: `install(ContentNegotiation) { }` ```kotlin fun <P, B, F> Application.install(plugin: Plugin<P, B, F>, configure: B.() -> Unit): F ``` (conceptually; `B` is the plugin's Configuration type.) - Receiver: the plugin's **Configuration** class. For ContentNegotiation that exposes converter-registration verbs like `json(...)`, `register(contentType, converter)`. - Inside: you don't just set flags — you **call registration functions** the Configuration declares. - Output/effect: the plugin is **installed into the application's pipeline** as a side effect; the call returns the plugin instance but you typically use it for its effect. ```kotlin fun Application.module() { install(ContentNegotiation) { json(Json { prettyPrint = true; ignoreUnknownKeys = true }) // nested settings DSL inside plugin DSL } } ``` Note the **nesting**: the standalone `Json { }` settings DSL is passed as an argument to `json(...)`, which is a verb of the ContentNegotiation Configuration. ## What the receiver type tells you The receiver type is the **menu** of the block. To know what is legal inside `install(X) { }`, look at `X`'s Configuration class — its public members are exactly the DSL verbs available. This is why IDE autocomplete inside the block lists the Configuration's API. ## Key differences - **Return vs side effect:** `Json { }` returns a value you hold; `install { }` mutates the application pipeline. - **Properties vs verbs:** settings DSLs mostly assign properties; plugin Configs often expose registration *functions*. - **Lifecycle:** a Json config is a plain object; an installed plugin participates in request handling. ## Same underlying mechanism Both are function types with receiver (`Builder.() -> Unit` / `Configuration.() -> Unit`) invoked via trailing-lambda syntax, with `apply`-style 'run lambda against a fresh object' internals. The difference is what happens to that object afterward. ### Key APIs/keywords - `Configuration.() -> Unit` plugin config lambda - `install(Plugin) { }` returns the plugin / configures the pipeline - `JsonBuilder.() -> Unit` settings lambda returns immutable Json - Receiver type = available DSL verbs/properties - Nesting a settings DSL inside a plugin DSL (`json(Json { })`)
- How do you discover what functions/properties are legal inside an install { } block?Look at the plugin's Configuration class — its public members are exactly the DSL verbs/properties available, which is also what IDE autocomplete shows.
- What is the main behavioral difference between Json { } and install { }?Json { } returns an immutable config you hold; install { } configures and registers a plugin into the application pipeline as a side effect.
Json { } is filling out a form you keep; install(Plugin) { } is configuring an appliance and bolting it into the building's wiring.
saying these in an interview costs you the question
- Saying install(Plugin) { } returns a config object you must keep and use like Json
- Thinking the install block can set arbitrary fields rather than the plugin's Configuration members
- Not realizing a settings DSL can be nested inside a plugin DSL
- Claiming the two use different language mechanisms