scalaintermediate

Context Parameters with using/given

Use Scala 3 context parameters: given instances, using clauses, context bounds, and implicit scope.

scala
import scala.concurrent.{ExecutionContext, Future}

// given: provide instances
trait Ordering[T]:
  def compare(a: T, b: T): Int
  extension (a: T)
    def <(b: T): Boolean = compare(a, b) < 0
    def >(b: T): Boolean = compare(a, b) > 0

given Ordering[Int] with
  def compare(a: Int, b: Int): Int = a - b

given Ordering[String] with
  def compare(a: String, b: String): Int = a.compareTo(b)

// Reverse ordering derived from existing
given reverseInt(using ord: Ordering[Int]): Ordering[Int] with
  def compare(a: Int, b: Int): Int = ord.compare(b, a)

// using: request instances
def max[T](a: T, b: T)(using ord: Ordering[T]): T =
  if ord.compare(a, b) >= 0 then a else b

def sort[T](items: List[T])(using ord: Ordering[T]): List[T] =
  items.sortWith((a, b) => ord.compare(a, b) < 0)

// Context bounds (syntactic sugar)
def minimum[T: Ordering](items: List[T]): T =
  items.reduce((a, b) => if summon[Ordering[T]].compare(a, b) <= 0 then a else b)

// Multiple context parameters
trait Show[T]:
  def show(value: T): String

given Show[Int] with
  def show(value: Int) = value.toString

given Show[String] with
  def show(value: String) = s"\"$value\""

def display[T: Ordering: Show](items: List[T]): String =
  val s = summon[Show[T]]
  sort(items).map(s.show).mkString(", ")

// Given for case classes
case class Config(host: String, port: Int, debug: Boolean)

given Config = Config("localhost", 8080, false)

def startServer(using config: Config): String =
  s"Starting ${config.host}:${config.port} (debug=${config.debug})"

// Given with import
object Implicits:
  given timeout: Int = 5000
  given retries: Int = 3

// Conditional givens
given [T](using ord: Ordering[T]): Ordering[List[T]] with
  def compare(a: List[T], b: List[T]): Int =
    a.zip(b).map((x, y) => ord.compare(x, y))
      .find(_ != 0).getOrElse(a.size - b.size)

given [T](using s: Show[T]): Show[List[T]] with
  def show(value: List[T]): String =
    value.map(s.show).mkString("[", ", ", "]")

// summon: retrieve given instance
def getOrdering[T](using ord: Ordering[T]): Ordering[T] = ord

@main def run(): Unit =
  // Basic usage
  println(s"Max(3, 7): ${max(3, 7)}")
  println(s"Max(\"a\", \"z\"): ${max("a", "z")}")

  // Sort
  println(s"Sorted: ${sort(List(3, 1, 4, 1, 5, 9))}")
  println(s"Min: ${minimum(List(5, 2, 8, 1, 9))}")

  // Display with multiple type classes
  println(s"Display: ${display(List(3, 1, 4, 1, 5))}")

  // Config
  println(startServer)

  // Override given locally
  {
    given Config = Config("0.0.0.0", 443, true)
    println(startServer)
  }

  // Derived instances
  println(s"List show: ${summon[Show[List[Int]]].show(List(1, 2, 3))}")
  println(s"List compare: ${summon[Ordering[List[Int]]].compare(List(1, 2), List(1, 3))}")

Use Cases

  • Type class instances
  • Dependency injection via givens
  • Extensible behavior without inheritance

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.