पाठ 14 / 25

Givens, Using Clauses and Type Classes

Use contextual abstractions to pass context and implement type classes.

Contextual abstractions in Scala 3

Scala 3 replaces Scala 2's overloaded implicit keyword with clearer constructs. A given instance defines a canonical value for a type, and a using clause declares that a method needs such a value from the context: def sorted(using ord: Ordering[A]). The compiler finds the given automatically at the call site, though you can still pass one explicitly with using. This is how Scala implements type classes: a trait such as trait Show[A] { def show(a: A): String } describes a capability, givens provide instances for specific types (given Show[Money]), and generic code requires the capability with [A: Show] (a context bound, shorthand for a using parameter). Type classes add behaviour to types you do not own, such as JSON encoding, ordering, equality and serialisation, without inheritance. derives clauses (case class Item(...) derives Codec) ask a library to generate instances automatically. Givens are imported explicitly with import x.given, so it stays clear where they come from. Use them for genuine context (an execution context, a configuration, a type class), not to hide ordinary parameters.

A Show type class with givens

Instances for built-in and domain types, and a generic function needing one.

trait Show[A]:
  extension (a: A) def show: String

given Show[Int] with
  extension (n: Int) def show: String = n.toString

case class Money(paise: Long)

given Show[Money] with
  extension (m: Money) def show: String = f"Rs ${m.paise / 100.0}%.2f"

given [A](using Show[A]): Show[List[A]] with      // derived instance for lists
  extension (xs: List[A]) def show: String = xs.map(_.show).mkString("[", ", ", "]")

def printAll[A: Show](items: A*): Unit =           // context bound = using Show[A]
  items.foreach(i => println(i.show))

printAll(Money(4950), Money(19900))               // Rs 49.50, Rs 199.00
println(List(Money(100), Money(250)).show)         // [Rs 1.00, Rs 2.50]

case class Gadget(name: String, pricePaise: Long)
given Ordering[Gadget] = Ordering.by[Gadget, Long](_.pricePaise)
val cheapestFirst = List(Gadget("ink", 19900), Gadget("pen", 4950)).sorted   // uses the given Ordering

A plug adapter in the hotel room

A using clause is a device that needs a socket adapter. Instead of carrying one in every call, the hotel room (the scope) provides a given adapter, and the compiler plugs it in for you.

त्वरित जाँच: What does a context bound such as [A: Show] mean?

  • A must extend Show
  • A is optional
  • Show is a subclass of A
  • The method needs a given Show[A] instance from the context
Answer

The method needs a given Show[A] instance from the context — Context bounds are shorthand for a using parameter of type Show[A].