scalaintermediate

Given Imports and Exports

Manage given instances with Scala 3 import syntax: selective imports, exports, and priorities.

scala
// Module with given instances
object JsonCodecs:
  trait Encoder[T]:
    def encode(value: T): String

  trait Decoder[T]:
    def decode(json: String): Either[String, T]

  given Encoder[Int] with
    def encode(value: Int): String = value.toString

  given Encoder[String] with
    def encode(value: String): String = s"\"$value\""

  given Encoder[Boolean] with
    def encode(value: Boolean): String = value.toString

  given Decoder[Int] with
    def decode(json: String): Either[String, Int] =
      json.toIntOption.toRight(s"Cannot parse '$json' as Int")

  given Decoder[String] with
    def decode(json: String): Either[String, String] =
      if json.startsWith("\"") && json.endsWith("\"") then
        Right(json.drop(1).dropRight(1))
      else Left(s"Expected string, got: $json")

object Formatters:
  trait Show[T]:
    def show(value: T): String

  // Priority: low-priority in trait, high-priority in companion
  trait LowPriorityShows:
    given Show[Any] with
      def show(value: Any): String = value.toString

  object Show extends LowPriorityShows:
    given Show[Int] with
      def show(value: Int): String = s"Int($value)"

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

    given Show[Double] with
      def show(value: Double): String = f"Double($value%.2f)"

// Using selective given imports
object App:
  // Import only given instances
  import JsonCodecs.{given JsonCodecs.Encoder[?]}
  import Formatters.Show
  import Formatters.Show.given

  def toJson[T](value: T)(using enc: JsonCodecs.Encoder[T]): String =
    enc.encode(value)

  def display[T](value: T)(using s: Show[T]): String =
    s.show(value)

  @main def run(): Unit =
    println(toJson(42))
    println(toJson("hello"))
    println(toJson(true))

    // Decoder usage
    import JsonCodecs.{given JsonCodecs.Decoder[?]}
    val decoded = summon[JsonCodecs.Decoder[Int]].decode("42")
    println(s"Decoded: $decoded")

    val failed = summon[JsonCodecs.Decoder[Int]].decode("abc")
    println(s"Failed: $failed")

    // Show with priority
    println(display(42))
    println(display("hello"))
    println(display(3.14))
    println(display(List(1, 2, 3)))  // falls back to Any

Use Cases

  • Organizing type class instances
  • Controlling given scope and priority
  • Module-level codec management

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.