scalaadvanced

Context Functions and Capability Pattern

Use Scala 3 context functions for dependency injection, capability passing, and builder DSLs.

scala
import scala.collection.mutable.ListBuffer

// Context function type: A ?=> B
trait Logger:
  def log(msg: String): Unit

class ConsoleLogger(prefix: String) extends Logger:
  def log(msg: String): Unit = println(s"[$prefix] $msg")

// Context function
type Logged[T] = Logger ?=> T

def doWork(input: String): Logged[String] =
  summon[Logger].log(s"Processing: $input")
  val result = input.toUpperCase
  summon[Logger].log(s"Result: $result")
  result

def pipeline(data: List[String]): Logged[List[String]] =
  summon[Logger].log(s"Pipeline started with ${data.size} items")
  val results = data.map(doWork)
  summon[Logger].log(s"Pipeline completed")
  results

// Builder DSL with context functions
class HtmlBuilder:
  private val buffer = ListBuffer[String]()
  private var indent = 0

  def tag(name: String)(content: HtmlBuilder ?=> Unit): Unit =
    buffer += s"${" " * indent}<$name>"
    indent += 2
    content(using this)
    indent -= 2
    buffer += s"${" " * indent}</$name>"

  def text(s: String): Unit =
    buffer += s"${" " * indent}$s"

  def result: String = buffer.mkString("\n")

def html(build: HtmlBuilder ?=> Unit): String =
  val builder = HtmlBuilder()
  build(using builder)
  builder.result

// Transaction context
trait Transaction:
  def execute(sql: String): Unit
  def rollback(): Unit

class DbTransaction extends Transaction:
  private val executed = ListBuffer[String]()
  def execute(sql: String): Unit =
    println(s"  SQL: $sql")
    executed += sql
  def rollback(): Unit =
    println(s"  Rolling back ${executed.size} operations")
    executed.clear()

type Transactional[T] = Transaction ?=> T

def insertUser(name: String): Transactional[Unit] =
  summon[Transaction].execute(s"INSERT INTO users VALUES ('$name')")

def insertOrder(userId: String, amount: Double): Transactional[Unit] =
  summon[Transaction].execute(
    s"INSERT INTO orders VALUES ('$userId', $amount)"
  )

def transferFunds(from: String, to: String, amount: Double): Transactional[Unit] =
  summon[Transaction].execute(s"UPDATE accounts SET balance = balance - $amount WHERE id = '$from'")
  summon[Transaction].execute(s"UPDATE accounts SET balance = balance + $amount WHERE id = '$to'")

@main def run(): Unit =
  // Logger context
  given Logger = ConsoleLogger("APP")
  val result = pipeline(List("hello", "world", "scala"))
  println(s"Results: $result")

  // HTML DSL
  val page = html {
    summon[HtmlBuilder].tag("html") {
      summon[HtmlBuilder].tag("body") {
        summon[HtmlBuilder].tag("h1") {
          summon[HtmlBuilder].text("Hello, Scala 3!")
        }
        summon[HtmlBuilder].tag("p") {
          summon[HtmlBuilder].text("Context functions are powerful.")
        }
      }
    }
  }
  println(page)

  // Transaction context
  given Transaction = DbTransaction()
  println("Running transaction:")
  insertUser("alice")
  insertOrder("alice", 99.99)
  transferFunds("alice", "bob", 50.0)

Use Cases

  • Dependency injection without frameworks
  • Builder DSLs with implicit context
  • Transaction management patterns

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.