scalaintermediate

Implicit Conversions in Scala 3

Use Scala 3 Conversion type class for safe implicit conversions and interop patterns.

scala
import scala.language.implicitConversions

// Scala 3 Conversion type class
case class Celsius(value: Double)
case class Fahrenheit(value: Double)
case class Kelvin(value: Double)

given Conversion[Celsius, Fahrenheit] with
  def apply(c: Celsius): Fahrenheit =
    Fahrenheit(c.value * 9.0 / 5.0 + 32)

given Conversion[Fahrenheit, Celsius] with
  def apply(f: Fahrenheit): Celsius =
    Celsius((f.value - 32) * 5.0 / 9.0)

given Conversion[Celsius, Kelvin] with
  def apply(c: Celsius): Kelvin =
    Kelvin(c.value + 273.15)

// Duration conversions
case class Seconds(value: Double)
case class Minutes(value: Double)
case class Hours(value: Double)

given Conversion[Minutes, Seconds] with
  def apply(m: Minutes): Seconds = Seconds(m.value * 60)

given Conversion[Hours, Seconds] with
  def apply(h: Hours): Seconds = Seconds(h.value * 3600)

given Conversion[Hours, Minutes] with
  def apply(h: Hours): Minutes = Minutes(h.value * 60)

// Java interop conversions
import java.util.{List as JList, Map as JMap, Optional}
import scala.jdk.CollectionConverters.*

extension [T](opt: Optional[T])
  def toScala: Option[T] =
    if opt.isPresent then Some(opt.get()) else None

extension [T](opt: Option[T])
  def toJava: Optional[T] = opt match
    case Some(v) => Optional.of(v)
    case None => Optional.empty()

// Numeric conversions with validation
object SafeConversions:
  opaque type PositiveInt = Int
  object PositiveInt:
    def apply(n: Int): Option[PositiveInt] =
      if n > 0 then Some(n) else None
    extension (p: PositiveInt)
      def value: Int = p

  given Conversion[PositiveInt, Int] with
    def apply(p: PositiveInt): Int = p

@main def run(): Unit =
  // Temperature
  val boiling: Celsius = Celsius(100)
  val boilingF: Fahrenheit = boiling  // automatic conversion
  val boilingK: Kelvin = boiling
  println(f"${boiling.value}°C = ${boilingF.value}%.1f°F = ${boilingK.value}%.2fK")

  val bodyTemp: Fahrenheit = Fahrenheit(98.6)
  val bodyCelsius: Celsius = bodyTemp
  println(f"${bodyTemp.value}°F = ${bodyCelsius.value}%.1f°C")

  // Duration
  def processWithTimeout(timeout: Seconds): Unit =
    println(f"Timeout: ${timeout.value}%.0f seconds")

  processWithTimeout(Minutes(5))    // auto-converted to 300 seconds
  processWithTimeout(Hours(1))      // auto-converted to 3600 seconds

  // Java interop
  val jOptional = Optional.of("hello")
  val scalaOption: Option[String] = jOptional.toScala
  println(s"Optional -> Option: $scalaOption")

  val back: Optional[String] = scalaOption.toJava
  println(s"Option -> Optional: $back")

Use Cases

  • Unit conversion libraries
  • Java-Scala interop
  • Domain type conversions

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.