kotlinadvanced

Context Receivers — Scoped Functions

Use context receivers for scoped function access: transaction contexts, logging contexts, and DSL building.

kotlin
// Context receivers (Kotlin 1.6.20+ experimental)
// Compile with: -Xcontext-receivers

// Logger context
interface Logger {
    fun log(message: String)
}

class ConsoleLogger : Logger {
    override fun log(message: String) = println("[LOG] $message")
}

// Transaction context
class Transaction(val id: String) {
    private val operations = mutableListOf<String>()
    var committed = false; private set

    fun addOperation(op: String) {
        check(!committed) { "Transaction already committed" }
        operations.add(op)
    }

    fun commit() {
        committed = true
        println("Transaction $id committed: ${operations.size} operations")
    }

    fun rollback() {
        operations.clear()
        println("Transaction $id rolled back")
    }
}

// Functions with context receivers
context(Logger)
fun processOrder(orderId: String) {
    log("Processing order: $orderId")
    // can use log() directly since Logger is in context
}

context(Logger, Transaction)
fun createOrder(userId: String, items: List<String>) {
    log("Creating order for user: $userId")
    addOperation("INSERT INTO orders ...")
    items.forEach { item ->
        log("Adding item: $item")
        addOperation("INSERT INTO order_items ($item)")
    }
}

context(Transaction)
fun saveAuditLog(action: String) {
    addOperation("INSERT INTO audit_log ('$action')")
}

// Context receiver on class
context(Logger)
class OrderService {
    fun placeOrder(userId: String, items: List<String>) {
        log("OrderService.placeOrder")
        // has Logger in scope
    }
}

// Context receiver with extension functions
context(Logger)
fun String.logAndUppercase(): String {
    log("Transforming: $this")
    return uppercase()
}

// Builder pattern with context
interface HtmlScope {
    fun tag(name: String, content: String)
    fun text(content: String)
}

class HtmlBuilder : HtmlScope {
    private val parts = mutableListOf<String>()
    override fun tag(name: String, content: String) {
        parts.add("<$name>$content</$name>")
    }
    override fun text(content: String) { parts.add(content) }
    fun build() = parts.joinToString("\n")
}

context(HtmlScope)
fun header(title: String) {
    tag("h1", title)
}

context(HtmlScope)
fun paragraph(text: String) {
    tag("p", text)
}

fun buildHtml(block: context(HtmlScope) () -> Unit): String {
    val builder = HtmlBuilder()
    block(builder)
    return builder.build()
}

// Simulated usage (requires -Xcontext-receivers)
fun main() {
    val logger = ConsoleLogger()

    // Provide context
    with(logger) {
        processOrder("ORD-001")

        val tx = Transaction("TX-001")
        with(tx) {
            createOrder("user-1", listOf("laptop", "mouse"))
            saveAuditLog("order_created")
            tx.commit()
        }
    }

    // HTML builder with context
    println("\n--- HTML ---")
    val html = buildHtml {
        header("Welcome")
        paragraph("This is a context receiver demo.")
        paragraph("Clean and type-safe!")
    }
    println(html)
}

Use Cases

  • Transaction and logging scope management
  • Type-safe builder patterns
  • Cross-cutting concern injection

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.