Contextual Abstractions
Scala 3 replaces Scala 2 "implicits" with clearer, intent-specific features: given, using, extension methods, type classes, context functions, and conversions.
Scala 3 replaces Scala 2 "implicits" with clearer, intent-specific features: given, using, extension methods, type classes, context functions, and conversions.
26.1 Type classes with given/using
// 1. The type class
trait Show[A]:
extension (a: A) def show: String
// 2. Instances
object Show:
given Show[Int] with
extension (a: Int) def show = s"Int($a)"
given Show[String] with
extension (a: String) def show = s"\"$a\""
given [A](using sa: Show[A]): Show[List[A]] with // derived instance
extension (as: List[A]) def show = as.map(_.show).mkString("[", ", ", "]")
// 3. Using instances
def printAll[A: Show](xs: List[A]): Unit = xs.foreach(x => println(x.show))
def describe[A](a: A)(using s: Show[A]): String = s"Value: ${a.show}"
List(1, 2, 3).show // [Int(1), Int(2), Int(3)]
describe("hi")
summon[Show[Int]] // retrieve an instance explicitly (Scala 2: implicitly)26.2 Named and anonymous givens, alias givens
given intOrdering: Ordering[Int] = Ordering.Int.reverse // alias given
given Ordering[String] = Ordering.String // anonymous
given ExecutionContext = ExecutionContext.global
lazy given Config = loadConfig() // lazily initialized26.3 Using clauses
case class Config(url: String, timeout: Int)
def connect(name: String)(using cfg: Config): String = s"$name -> ${cfg.url}"
def run()(using Config) = connect("db") // anonymous using parameter, passed along
given Config = Config("jdbc:pg://localhost", 30)
run()
connect("cache")(using Config("redis://", 5)) // explicit26.4 Given imports
object Instances:
given Show[Boolean] with { extension (b: Boolean) def show = if b then "yes" else "no" }
import Instances.given // all givens
import Instances.{given Show[Boolean]} // a specific one
import Instances.* // does NOT import givens in Scala 326.5 Resolution rules
The compiler searches for givens in: (1) the lexical scope (local definitions, imports), then (2) the implicit scope of the type — companion objects of the type class and of the type arguments. Ambiguities are compile errors; more specific instances win.
26.6 Type class derivation
Libraries provide derived: derives Codec.AsObject (circe), derives ReadWriter (upickle), derives Eq, Order (kittens). See §32.4 for writing derived.
case class Person(name: String, age: Int) derives CanEqual, Show // Show needs a `derived` method
enum Color derives CanEqual:
case Red, Green26.7 Context functions
Used for builders/DSLs and capability passing.
type Transactional[A] = Transaction ?=> A
def transfer(amount: Int): Transactional[Unit] =
summon[Transaction].log(s"transfer $amount")
def inTx[A](body: Transactional[A]): A =
given Transaction = Transaction()
body26.8 Implicit conversions (use sparingly)
import scala.language.implicitConversions
given Conversion[String, Int] = _.length
val n: Int = "hello" // 526.9 Multiversal equality
import scala.language.strictEquality
case class Id(v: Int) derives CanEqual
Id(1) == Id(2) // OK
// Id(1) == "x" // compile error under strictEquality26.10 By-name context parameters & NotGiven
import scala.util.NotGiven
def onlyIfNoOrdering[A](using NotGiven[Ordering[A]]): String = "no ordering available"