Scala 3 Advanced Features: Union Types, Intersection Types, and Opaque Types

Scala 3 introduces revolutionary type system enhancements that provide unprecedented expressiveness and safety. This comprehensive lesson explores union types, intersection types, opaque types, and other advanced features that make Scala 3 a cutting-edge programming language for building robust, type-safe applications.

Union Types: Expressing Alternatives

Basic Union Type Concepts

// Union types represent values that can be one of several types
type StringOrInt = String | Int
type NumberOrBoolean = Int | Double | Boolean
type Result[T] = T | Error

case class Error(message: String, code: Int)

// Function using union types
def processValue(value: StringOrInt): String = value match {
  case s: String => s"String: $s"
  case i: Int => s"Integer: $i"
}

// Multiple alternatives
def formatNumber(value: NumberOrBoolean): String = value match {
  case i: Int => f"Integer: $i%d"
  case d: Double => f"Double: $d%.2f"
  case b: Boolean => s"Boolean: $b"
}

// Result type with error handling
def divideNumbers(a: Double, b: Double): Result[Double] = {
  if (b == 0) Error("Division by zero", 400)
  else a / b
}

// Working with result types
def handleResult[T](result: Result[T]): String = result match {
  case error: Error => s"Error ${error.code}: ${error.message}"
  case value: T => s"Success: $value"
}

// Example usage
val stringValue: StringOrInt = "Hello"
val intValue: StringOrInt = 42

println(processValue(stringValue)) // String: Hello
println(processValue(intValue))    // Integer: 42

val divisionResult = divideNumbers(10.0, 2.0)
println(handleResult(divisionResult)) // Success: 5.0

val errorResult = divideNumbers(10.0, 0.0)
println(handleResult(errorResult)) // Error 400: Division by zero

Advanced Union Type Patterns

// Complex union types for domain modeling
object DomainModeling {

  // Payment method union type
  sealed trait PaymentMethod
  case class CreditCard(number: String, expiryMonth: Int, expiryYear: Int, cvv: String) extends PaymentMethod
  case class PayPal(email: String) extends PaymentMethod
  case class BankTransfer(accountNumber: String, routingNumber: String) extends PaymentMethod
  case class Cryptocurrency(address: String, currency: String) extends PaymentMethod

  type PaymentOrError = PaymentMethod | PaymentError

  sealed trait PaymentError
  case class InvalidCardNumber(providedNumber: String) extends PaymentError
  case class ExpiredCard(expiryMonth: Int, expiryYear: Int) extends PaymentError
  case class InvalidEmail(email: String) extends PaymentError
  case class InsufficientFunds(available: BigDecimal, required: BigDecimal) extends PaymentError

  // Validation using union types
  def validatePaymentMethod(input: String, methodType: String): PaymentOrError = {
    methodType.toLowerCase match {
      case "creditcard" =>
        if (input.matches("\\d{16}")) {
          CreditCard(input, 12, 2025, "123")
        } else {
          InvalidCardNumber(input)
        }

      case "paypal" =>
        if (input.contains("@") && input.contains(".")) {
          PayPal(input)
        } else {
          InvalidEmail(input)
        }

      case "crypto" =>
        if (input.length >= 26 && input.length <= 35) {
          Cryptocurrency(input, "BTC")
        } else {
          InvalidCardNumber(input) // Reusing error type
        }

      case _ =>
        InvalidEmail("Unknown payment method")
    }
  }

  // Processing payment with union types
  def processPayment(method: PaymentOrError, amount: BigDecimal): String = method match {
    case card: CreditCard =>
      s"Processing $$${amount} via credit card ending in ${card.number.takeRight(4)}"
    case paypal: PayPal =>
      s"Processing $$${amount} via PayPal account ${paypal.email}"
    case bank: BankTransfer =>
      s"Processing $$${amount} via bank transfer to ${bank.accountNumber}"
    case crypto: Cryptocurrency =>
      s"Processing $$${amount} via ${crypto.currency} to address ${crypto.address.take(8)}..."
    case error: PaymentError =>
      s"Payment failed: ${formatPaymentError(error)}"
  }

  private def formatPaymentError(error: PaymentError): String = error match {
    case InvalidCardNumber(number) => s"Invalid card number: $number"
    case ExpiredCard(month, year) => s"Card expired: $month/$year"
    case InvalidEmail(email) => s"Invalid email: $email"
    case InsufficientFunds(available, required) => 
      s"Insufficient funds: $$${available} available, $$${required} required"
  }

  // Union types with generic parameters
  type ApiResponse[T] = T | ApiError

  sealed trait ApiError
  case class NetworkError(message: String, retryAfter: Option[Int] = None) extends ApiError
  case class ValidationError(field: String, message: String) extends ApiError
  case class AuthenticationError(message: String) extends ApiError
  case class ServerError(code: Int, message: String) extends ApiError

  // API client using union types
  class ApiClient {

    def fetchUser(id: String): ApiResponse[User] = {
      if (id.nonEmpty && id.forall(_.isDigit)) {
        User(id, s"User$id", s"user$id@example.com")
      } else {
        ValidationError("id", "User ID must be numeric")
      }
    }

    def updateUser(user: User): ApiResponse[User] = {
      if (user.email.contains("@")) {
        user.copy(name = user.name.toUpperCase)
      } else {
        ValidationError("email", "Invalid email format")
      }
    }

    def deleteUser(id: String): ApiResponse[Unit] = {
      if (id == "admin") {
        AuthenticationError("Cannot delete admin user")
      } else {
        ()
      }
    }
  }

  case class User(id: String, name: String, email: String)

  // Helper functions for working with API responses
  extension [T](response: ApiResponse[T]) {
    def map[U](f: T => U): ApiResponse[U] = response match {
      case value: T => f(value)
      case error: ApiError => error
    }

    def flatMap[U](f: T => ApiResponse[U]): ApiResponse[U] = response match {
      case value: T => f(value)
      case error: ApiError => error
    }

    def fold[U](onError: ApiError => U, onSuccess: T => U): U = response match {
      case error: ApiError => onError(error)
      case value: T => onSuccess(value)
    }

    def getOrElse(default: T): T = response match {
      case value: T => value
      case _: ApiError => default
    }

    def isSuccess: Boolean = response match {
      case _: T => true
      case _: ApiError => false
    }

    def isError: Boolean = !isSuccess
  }
}

Intersection Types: Combining Capabilities

Basic Intersection Type Usage

// Intersection types combine multiple types
trait Readable {
  def read(): String
}

trait Writable {
  def write(content: String): Unit
}

trait Seekable {
  def seek(position: Long): Unit
  def position: Long
}

// Intersection type combining multiple traits
type ReadWrite = Readable & Writable
type FullAccess = Readable & Writable & Seekable

// Implementation of intersection types
class FileHandler extends Readable, Writable, Seekable {
  private var content = ""
  private var pos = 0L

  def read(): String = content
  def write(content: String): Unit = { this.content = content }
  def seek(position: Long): Unit = { this.pos = position }
  def position: Long = pos
}

// Function requiring intersection type
def processFile(file: ReadWrite): String = {
  val content = file.read()
  file.write(content.toUpperCase)
  file.read()
}

// Advanced intersection with generic types
trait Serializable[T] {
  def serialize(value: T): String
  def deserialize(data: String): T
}

trait Validatable[T] {
  def validate(value: T): Boolean
  def getValidationErrors(value: T): List[String]
}

trait Cacheable[T] {
  def cacheKey(value: T): String
  def cacheTTL: Int
}

// Complex intersection type
type DataProcessor[T] = Serializable[T] & Validatable[T] & Cacheable[T]

// Implementation
class UserProcessor extends Serializable[User], Validatable[User], Cacheable[User] {

  def serialize(user: User): String = 
    s"${user.id}|${user.name}|${user.email}"

  def deserialize(data: String): User = {
    val parts = data.split("\\|")
    User(parts(0), parts(1), parts(2))
  }

  def validate(user: User): Boolean = 
    user.name.nonEmpty && user.email.contains("@")

  def getValidationErrors(user: User): List[String] = {
    var errors = List.empty[String]
    if (user.name.isEmpty) errors = "Name cannot be empty" :: errors
    if (!user.email.contains("@")) errors = "Invalid email format" :: errors
    errors
  }

  def cacheKey(user: User): String = s"user:${user.id}"
  def cacheTTL: Int = 300 // 5 minutes
}

// Using intersection types in generic contexts
def processData[T](data: T, processor: DataProcessor[T]): Either[List[String], String] = {
  if (processor.validate(data)) {
    Right(processor.serialize(data))
  } else {
    Left(processor.getValidationErrors(data))
  }
}

Structural Types and Intersection

// Structural types with intersection
object StructuralIntersection {

  // Duck typing with intersection
  type Drawable = { def draw(): Unit }
  type Movable = { def move(x: Int, y: Int): Unit }
  type Interactive = Drawable & Movable

  class Circle(var x: Int, var y: Int, val radius: Int) {
    def draw(): Unit = println(s"Drawing circle at ($x, $y) with radius $radius")
    def move(newX: Int, newY: Int): Unit = { x = newX; y = newY }
  }

  class Rectangle(var x: Int, var y: Int, val width: Int, val height: Int) {
    def draw(): Unit = println(s"Drawing rectangle at ($x, $y) ${width}x${height}")
    def move(newX: Int, newY: Int): Unit = { x = newX; y = newY }
  }

  // Function working with intersection type
  def manipulateShape(shape: Interactive): Unit = {
    shape.draw()
    shape.move(10, 20)
    shape.draw()
  }

  // Advanced structural intersection
  type Configurable = { 
    def configure(config: Map[String, Any]): Unit
    def getConfig(): Map[String, Any]
  }

  type Monitorable = {
    def getMetrics(): Map[String, Double]
    def healthCheck(): Boolean
  }

  type ManagedComponent = Configurable & Monitorable

  class DatabaseConnection extends {
    private var config = Map.empty[String, Any]
    private var connectionCount = 0.0
    private var queryTime = 0.0

    def configure(newConfig: Map[String, Any]): Unit = {
      config = newConfig
    }

    def getConfig(): Map[String, Any] = config

    def getMetrics(): Map[String, Double] = Map(
      "connections" -> connectionCount,
      "avgQueryTime" -> queryTime
    )

    def healthCheck(): Boolean = true

    def executeQuery(sql: String): Unit = {
      connectionCount += 1
      queryTime = scala.util.Random.nextDouble() * 100
    }
  }

  // Management function
  def manageComponent(component: ManagedComponent): Unit = {
    // Configure
    component.configure(Map("timeout" -> 30, "maxConnections" -> 100))

    // Monitor
    if (component.healthCheck()) {
      val metrics = component.getMetrics()
      println(s"Component healthy. Metrics: $metrics")
    } else {
      println("Component unhealthy!")
    }
  }
}

Opaque Types: Type-Safe Abstractions

Basic Opaque Type Definitions

// Opaque types provide zero-cost abstractions
object OpaqueTypeBasics {

  // Basic opaque type
  opaque type UserId = String

  object UserId {
    def apply(value: String): UserId = value
    def fromString(value: String): Option[UserId] = 
      if (value.nonEmpty && value.forall(_.isLetterOrDigit)) Some(value) else None
  }

  extension (userId: UserId) {
    def value: String = userId
    def isAdmin: Boolean = userId.startsWith("admin")
  }

  // Multiple opaque types for type safety
  opaque type EmailAddress = String
  opaque type PhoneNumber = String
  opaque type OrderId = Long
  opaque type ProductId = String

  object EmailAddress {
    def apply(email: String): EmailAddress = email
    def fromString(email: String): Option[EmailAddress] = 
      if (email.contains("@") && email.contains(".")) Some(email) else None
  }

  object PhoneNumber {
    def apply(phone: String): PhoneNumber = phone
    def fromString(phone: String): Option[PhoneNumber] = 
      if (phone.matches("\\+?\\d{10,15}")) Some(phone) else None
  }

  object OrderId {
    def apply(id: Long): OrderId = id
    def generate(): OrderId = System.currentTimeMillis()
  }

  object ProductId {
    def apply(id: String): ProductId = id
    def fromSKU(sku: String): ProductId = s"prod_$sku"
  }

  // Extension methods for opaque types
  extension (email: EmailAddress) {
    def domain: String = email.split("@").last
    def localPart: String = email.split("@").head
    def isGmail: Boolean = domain == "gmail.com"
  }

  extension (phone: PhoneNumber) {
    def formatted: String = {
      val digits = phone.replaceAll("\\D", "")
      if (digits.length == 10) {
        s"(${digits.take(3)}) ${digits.slice(3, 6)}-${digits.drop(6)}"
      } else {
        phone
      }
    }
    def countryCode: Option[String] = 
      if (phone.startsWith("+")) Some(phone.takeWhile(_ != ' ').drop(1)) else None
  }

  extension (orderId: OrderId) {
    def timestamp: Long = orderId
    def isRecent: Boolean = System.currentTimeMillis() - orderId < 24 * 60 * 60 * 1000 // 24 hours
  }

  extension (productId: ProductId) {
    def category: String = productId.split("_").headOption.getOrElse("unknown")
    def sku: String = productId.split("_").drop(1).mkString("_")
  }

  // Domain model using opaque types
  case class User(
    id: UserId,
    email: EmailAddress,
    phone: Option[PhoneNumber]
  )

  case class Order(
    id: OrderId,
    userId: UserId,
    products: List[ProductId],
    total: BigDecimal
  )

  // Business logic with type safety
  def createUser(
    idStr: String,
    emailStr: String,
    phoneStr: Option[String]
  ): Either[String, User] = {

    for {
      userId <- UserId.fromString(idStr).toRight("Invalid user ID")
      email <- EmailAddress.fromString(emailStr).toRight("Invalid email")
      phone <- phoneStr.map(PhoneNumber.fromString).sequence.toRight("Invalid phone number")
    } yield User(userId, email, phone)
  }

  def createOrder(user: User, productIds: List[String]): Order = {
    Order(
      id = OrderId.generate(),
      userId = user.id,
      products = productIds.map(ProductId.apply),
      total = BigDecimal(99.99)
    )
  }

  // Helper extension for Option sequence
  extension [T](option: Option[Option[T]]) {
    def sequence: Option[Option[T]] = option match {
      case Some(Some(value)) => Some(Some(value))
      case Some(None) => None
      case None => Some(None)
    }
  }
}

Advanced Opaque Type Patterns

// Complex opaque types with validation and operations
object AdvancedOpaqueTypes {

  // Financial types with precision
  opaque type Money = BigDecimal
  opaque type Currency = String
  opaque type ExchangeRate = BigDecimal

  object Money {
    def apply(amount: BigDecimal): Money = amount.setScale(2, BigDecimal.RoundingMode.HALF_UP)
    def apply(amount: Double): Money = Money(BigDecimal(amount))
    def zero: Money = Money(BigDecimal(0))

    def fromString(str: String): Option[Money] = {
      try {
        val amount = BigDecimal(str)
        if (amount >= 0) Some(Money(amount)) else None
      } catch {
        case _: NumberFormatException => None
      }
    }
  }

  object Currency {
    val USD: Currency = "USD"
    val EUR: Currency = "EUR"
    val GBP: Currency = "GBP"
    val JPY: Currency = "JPY"

    def apply(code: String): Option[Currency] = {
      val validCurrencies = Set("USD", "EUR", "GBP", "JPY", "CAD", "AUD", "CHF")
      if (validCurrencies.contains(code.toUpperCase)) Some(code.toUpperCase) else None
    }
  }

  object ExchangeRate {
    def apply(rate: BigDecimal): ExchangeRate = rate.setScale(6, BigDecimal.RoundingMode.HALF_UP)
    def apply(rate: Double): ExchangeRate = ExchangeRate(BigDecimal(rate))
  }

  // Extensions for money operations
  extension (money: Money) {
    def value: BigDecimal = money
    def cents: Long = (money * 100).toLong
    def +(other: Money): Money = Money(money + other)
    def -(other: Money): Money = Money(money - other)
    def *(multiplier: BigDecimal): Money = Money(money * multiplier)
    def /(divisor: BigDecimal): Money = Money(money / divisor)

    def format(currency: Currency): String = {
      val symbol = currency match {
        case "USD" => "$"
        case "EUR" => "€"
        case "GBP" => "£"
        case "JPY" => "¥"
        case _ => currency + " "
      }
      s"$symbol${money.setScale(2)}"
    }

    def convert(rate: ExchangeRate): Money = Money(money * rate)

    def isPositive: Boolean = money > 0
    def isZero: Boolean = money == 0
    def isNegative: Boolean = money < 0
  }

  extension (currency: Currency) {
    def code: String = currency
    def symbol: String = currency match {
      case "USD" => "$"
      case "EUR" => "€"
      case "GBP" => "£"
      case "JPY" => "¥"
      case _ => currency
    }
  }

  extension (rate: ExchangeRate) {
    def value: BigDecimal = rate
    def inverse: ExchangeRate = ExchangeRate(BigDecimal(1) / rate)
  }

  // Money amount with currency
  case class CurrencyAmount(amount: Money, currency: Currency) {
    def format: String = amount.format(currency)
    def convert(toCurrency: Currency, rate: ExchangeRate): CurrencyAmount = 
      CurrencyAmount(amount.convert(rate), toCurrency)
  }

  // Coordinates with type safety
  opaque type Latitude = Double
  opaque type Longitude = Double
  opaque type Altitude = Double

  object Latitude {
    def apply(value: Double): Option[Latitude] = 
      if (value >= -90.0 && value <= 90.0) Some(value) else None

    def fromDegrees(degrees: Int, minutes: Int, seconds: Double, hemisphere: Char): Option[Latitude] = {
      val decimal = degrees + minutes / 60.0 + seconds / 3600.0
      val signed = if (hemisphere.toUpper == 'S') -decimal else decimal
      Latitude(signed)
    }
  }

  object Longitude {
    def apply(value: Double): Option[Longitude] = 
      if (value >= -180.0 && value <= 180.0) Some(value) else None

    def fromDegrees(degrees: Int, minutes: Int, seconds: Double, hemisphere: Char): Option[Longitude] = {
      val decimal = degrees + minutes / 60.0 + seconds / 3600.0
      val signed = if (hemisphere.toUpper == 'W') -decimal else decimal
      Longitude(signed)
    }
  }

  object Altitude {
    def apply(value: Double): Altitude = value
    def seaLevel: Altitude = 0.0
    def fromFeet(feet: Double): Altitude = feet * 0.3048
  }

  extension (lat: Latitude) {
    def value: Double = lat
    def degrees: Int = lat.toInt
    def minutes: Int = ((lat.abs % 1) * 60).toInt
    def seconds: Double = (((lat.abs % 1) * 60) % 1) * 60
    def hemisphere: Char = if (lat >= 0) 'N' else 'S'
    def toDMS: String = s"${degrees.abs}°${minutes}'${seconds}\"${hemisphere}"
  }

  extension (lon: Longitude) {
    def value: Double = lon
    def degrees: Int = lon.toInt
    def minutes: Int = ((lon.abs % 1) * 60).toInt
    def seconds: Double = (((lon.abs % 1) * 60) % 1) * 60
    def hemisphere: Char = if (lon >= 0) 'E' else 'W'
    def toDMS: String = s"${degrees.abs}°${minutes}'${seconds}\"${hemisphere}"
  }

  extension (alt: Altitude) {
    def value: Double = alt
    def meters: Double = alt
    def feet: Double = alt / 0.3048
    def isAboveSeaLevel: Boolean = alt > 0
    def isBelowSeaLevel: Boolean = alt < 0
  }

  // Geographic coordinate system
  case class Coordinates(
    latitude: Latitude,
    longitude: Longitude,
    altitude: Option[Altitude] = None
  ) {

    def distanceTo(other: Coordinates): Double = {
      // Haversine formula for great circle distance
      val R = 6371000 // Earth's radius in meters
      val lat1Rad = math.toRadians(latitude.value)
      val lat2Rad = math.toRadians(other.latitude.value)
      val deltaLatRad = math.toRadians(other.latitude.value - latitude.value)
      val deltaLonRad = math.toRadians(other.longitude.value - longitude.value)

      val a = math.sin(deltaLatRad / 2) * math.sin(deltaLatRad / 2) +
              math.cos(lat1Rad) * math.cos(lat2Rad) *
              math.sin(deltaLonRad / 2) * math.sin(deltaLonRad / 2)
      val c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))

      R * c
    }

    def format: String = {
      val altStr = altitude.map(alt => s", ${alt.meters}m").getOrElse("")
      s"${latitude.toDMS}, ${longitude.toDMS}$altStr"
    }
  }

  // Time types with precision
  opaque type Timestamp = Long
  opaque type Duration = Long
  opaque type TimeZoneOffset = Int

  object Timestamp {
    def now(): Timestamp = System.currentTimeMillis()
    def apply(millis: Long): Timestamp = millis
    def fromInstant(instant: java.time.Instant): Timestamp = instant.toEpochMilli
  }

  object Duration {
    def apply(millis: Long): Duration = millis
    def seconds(seconds: Long): Duration = seconds * 1000
    def minutes(minutes: Long): Duration = minutes * 60 * 1000
    def hours(hours: Long): Duration = hours * 60 * 60 * 1000
    def days(days: Long): Duration = days * 24 * 60 * 60 * 1000
  }

  object TimeZoneOffset {
    def apply(offsetMinutes: Int): TimeZoneOffset = offsetMinutes
    def UTC: TimeZoneOffset = 0
    def EST: TimeZoneOffset = -5 * 60
    def PST: TimeZoneOffset = -8 * 60
  }

  extension (timestamp: Timestamp) {
    def value: Long = timestamp
    def +(duration: Duration): Timestamp = Timestamp(timestamp + duration)
    def -(duration: Duration): Timestamp = Timestamp(timestamp - duration)
    def -(other: Timestamp): Duration = Duration(timestamp - other)
    def toInstant: java.time.Instant = java.time.Instant.ofEpochMilli(timestamp)
    def format: String = toInstant.toString
  }

  extension (duration: Duration) {
    def value: Long = duration
    def +(other: Duration): Duration = Duration(duration + other)
    def -(other: Duration): Duration = Duration(duration - other)
    def *(multiplier: Int): Duration = Duration(duration * multiplier)
    def /(divisor: Int): Duration = Duration(duration / divisor)

    def toSeconds: Long = duration / 1000
    def toMinutes: Long = duration / (60 * 1000)
    def toHours: Long = duration / (60 * 60 * 1000)
    def toDays: Long = duration / (24 * 60 * 60 * 1000)

    def format: String = {
      val days = toDays
      val hours = (toHours % 24)
      val minutes = (toMinutes % 60)
      val seconds = (toSeconds % 60)

      if (days > 0) s"${days}d ${hours}h ${minutes}m ${seconds}s"
      else if (hours > 0) s"${hours}h ${minutes}m ${seconds}s"
      else if (minutes > 0) s"${minutes}m ${seconds}s"
      else s"${seconds}s"
    }
  }

  extension (offset: TimeZoneOffset) {
    def value: Int = offset
    def toHours: Int = offset / 60
    def toMinutes: Int = offset % 60
    def format: String = {
      val hours = toHours
      val minutes = toMinutes.abs
      val sign = if (offset >= 0) "+" else "-"
      f"$sign$hours%02d:$minutes%02d"
    }
  }
}

Inline Definitions and Transparent Types

Inline Functions and Values

// Inline definitions for compile-time optimization
object InlineDefinitions {

  // Inline values - computed at compile time
  inline val MAX_RETRIES = 3
  inline val DEFAULT_TIMEOUT = 30000L
  inline val PI_PRECISE = 3.141592653589793

  // Inline functions - expanded at call site
  inline def square(x: Double): Double = x * x
  inline def cube(x: Double): Double = x * x * x
  inline def max(a: Int, b: Int): Int = if (a > b) a else b
  inline def min(a: Int, b: Int): Int = if (a < b) a else b

  // Inline functions with type parameters
  inline def identity[T](x: T): T = x
  inline def const[T, U](x: T)(y: U): T = x
  inline def swap[T, U](pair: (T, U)): (U, T) = (pair._2, pair._1)

  // Complex inline computations
  inline def factorial(n: Int): Long = {
    if (n <= 1) 1L
    else n * factorial(n - 1)
  }

  inline def fibonacci(n: Int): Long = {
    if (n <= 1) n.toLong
    else fibonacci(n - 1) + fibonacci(n - 2)
  }

  // Inline conditional compilation
  inline def debugPrint(message: String): Unit = {
    inline if (scala.util.Properties.propOrElse("debug", "false") == "true") {
      println(s"DEBUG: $message")
    }
  }

  inline def assertDebug(condition: Boolean, message: String): Unit = {
    inline if (scala.util.Properties.propOrElse("debug", "false") == "true") {
      assert(condition, message)
    }
  }

  // Inline pattern matching
  inline def processValue(value: Any): String = value match {
    case i: Int => s"Integer: $i"
    case s: String => s"String: $s"
    case d: Double => s"Double: $d"
    case _ => "Unknown type"
  }

  // Inline with dependent types
  inline def selectType[T](inline evidence: Boolean): Any = {
    inline if (evidence) {
      null.asInstanceOf[T]
    } else {
      throw new RuntimeException("Type not available")
    }
  }

  // Performance-critical inline functions
  inline def fastModPowerOfTwo(value: Int, mod: Int): Int = {
    // Only works when mod is power of 2
    value & (mod - 1)
  }

  inline def isPowerOfTwo(n: Int): Boolean = {
    n > 0 && (n & (n - 1)) == 0
  }

  inline def nextPowerOfTwo(n: Int): Int = {
    if (n <= 1) 1
    else {
      var result = 1
      while (result < n) result <<= 1
      result
    }
  }

  // Inline builders for DSLs
  inline def html(inline content: String): String = {
    s"<html><body>$content</body></html>"
  }

  inline def div(inline className: String, inline content: String): String = {
    s"""<div class="$className">$content</div>"""
  }

  inline def span(inline content: String): String = {
    s"<span>$content</span>"
  }

  // Usage examples
  val result1 = square(5.0) // Inlined to: 5.0 * 5.0
  val result2 = max(10, 20) // Inlined to: if (10 > 20) 10 else 20
  val fact5 = factorial(5) // Computed at compile time: 120L

  debugPrint("This will only print if debug=true")

  val htmlDoc = html(
    div("container",
      span("Hello, World!")
    )
  )
}

// Transparent inline for type-level programming
object TransparentInline {

  // Transparent inline functions preserve exact types
  transparent inline def selectFirst[T, U](x: T, y: U): T | U = x
  transparent inline def selectSecond[T, U](x: T, y: U): T | U = y

  transparent inline def chooseType[T, U](inline condition: Boolean): Any =
    inline if (condition) null.asInstanceOf[T] else null.asInstanceOf[U]

  // Type-level computations
  transparent inline def sizeOf[T]: Int = {
    inline scala.compiletime.erasedValue[T] match {
      case _: Byte => 1
      case _: Short => 2
      case _: Int => 4
      case _: Long => 8
      case _: Float => 4
      case _: Double => 8
      case _: Boolean => 1
      case _: Char => 2
      case _ => -1 // Unknown size
    }
  }

  // Compile-time string manipulation
  import scala.compiletime.{constValue, constValueTuple}

  transparent inline def stringLength[S <: String & Singleton]: Int = {
    constValue[S].length
  }

  transparent inline def stringConcat[S1 <: String & Singleton, S2 <: String & Singleton]: String = {
    constValue[S1] + constValue[S2]
  }

  // Type-level list operations
  transparent inline def tupleHead[H, T <: Tuple]: H = {
    inline scala.compiletime.erasedValue[H *: T] match {
      case _: (h *: t) => null.asInstanceOf[H]
    }
  }

  transparent inline def tupleTail[H, T <: Tuple]: T = {
    null.asInstanceOf[T]
  }

  // Compile-time validation
  inline def validateRange(inline value: Int, inline min: Int, inline max: Int): Int = {
    inline if (value < min || value > max) {
      scala.compiletime.error(s"Value $value is outside range [$min, $max]")
    }
    value
  }

  inline def validateNonEmpty[S <: String & Singleton]: S = {
    inline if (constValue[S].isEmpty) {
      scala.compiletime.error("String cannot be empty")
    }
    constValue[S]
  }

  // Usage examples
  val intSize = sizeOf[Int] // 4
  val doubleSize = sizeOf[Double] // 8

  val len = stringLength["Hello"] // 5
  val combined = stringConcat["Hello", "World"] // "HelloWorld"

  val validValue = validateRange(15, 10, 20) // OK
  // val invalidValue = validateRange(25, 10, 20) // Compile error

  val validString = validateNonEmpty["Hello"] // OK
  // val invalidString = validateNonEmpty[""] // Compile error
}

Conclusion

Scala 3's advanced type system features provide unprecedented expressiveness and safety for building robust applications. Key concepts include:

Union Types:

  • Express alternative types naturally with | syntax
  • Eliminate complex Either chains and option types
  • Model domain errors and success states cleanly
  • Enable functional error handling patterns

Intersection Types:

  • Combine multiple capabilities with & syntax
  • Create powerful abstractions through composition
  • Support structural typing for duck typing patterns
  • Enable mixin-style programming with type safety

Opaque Types:

  • Create zero-cost type abstractions
  • Provide compile-time safety without runtime overhead
  • Enable domain-specific type systems
  • Support gradual type refinement and validation

Inline Definitions:

  • Optimize performance through compile-time expansion
  • Enable metaprogramming and code generation
  • Support conditional compilation and feature flags
  • Create efficient domain-specific languages

Advanced Patterns:

  • Combine features for sophisticated type modeling
  • Create type-safe APIs with minimal boilerplate
  • Enable compile-time validation and optimization
  • Support gradual migration from Scala 2

Best Practices:

  • Use union types for error handling and alternatives
  • Apply intersection types for capability composition
  • Leverage opaque types for domain modeling
  • Employ inline definitions for performance-critical code
  • Combine features thoughtfully to avoid complexity

Migration Strategies:

  • Introduce opaque types to replace type aliases
  • Convert Either chains to union types gradually
  • Use intersection types instead of cake pattern
  • Apply inline definitions to hot code paths

Performance Considerations:

  • Union types have minimal runtime overhead
  • Intersection types enable efficient capability stacking
  • Opaque types provide zero-cost abstractions
  • Inline definitions eliminate function call overhead

Scala 3's type system advances represent a significant evolution in programming language design, offering developers powerful tools for expressing complex domain models while maintaining excellent performance characteristics and compile-time safety guarantees.