0Pricing
Scala for Backend Engineering & Functional Programming · Aula

Tipos opacos

Abstrações sem custo

Tipos opacos é uma aula grátis de Scala for Backend Engineering & Functional Programming no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Scala for Backend Engineering & Functional Programming, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Scala for Backend Engineering & Functional Programming inclui 4 aulas no total.

Partes desta aula ainda não foram traduzidas e aparecem em inglês.

What Are Opaque Types?

Opaque types are a Scala 3 feature for creating zero-cost abstractions. They give a distinct type at compile time but compile down to the underlying type with no runtime wrapping.

  • Type safety of a wrapper class.
  • Zero allocation overhead.
object Distances:
  opaque type Meters = Double

object Main:
  def main(args: Array[String]): Unit =
    println("Opaque types compile to their underlying type")

Declaring an Opaque Type

Declare an opaque type inside an object or class. Outside that scope, Meters and Double are treated as different types.

object Distances:
  opaque type Meters = Double
  def meters(d: Double): Meters = d
  def toDouble(m: Meters): Double = m

object Main:
  def main(args: Array[String]): Unit =
    val d = Distances.meters(5.0)
    println(Distances.toDouble(d))

The Boundary of Opacity

Inside the defining scope, the opaque type and its underlying type are interchangeable. Outside, only the opaque type is visible, so you cannot accidentally pass a raw Double where Meters is required.

object Distances:
  opaque type Meters = Double
  def apply(d: Double): Meters = d
  def show(m: Meters): String = s"$m m"

object Main:
  def main(args: Array[String]): Unit =
    val m = Distances(10.0)
    println(Distances.show(m))

Adding Extension Methods

Give opaque types behavior with extension methods defined in the same scope. Callers get convenient operations without exposing the underlying type.

object Distances:
  opaque type Meters = Double
  def apply(d: Double): Meters = d
  extension (m: Meters)
    def +(other: Meters): Meters = m + other
    def value: Double = m

object Main:
  def main(args: Array[String]): Unit =
    val total = Distances(3.0) + Distances(4.0)
    println(total.value)

Preventing Mixups

The big win: two opaque types over the same underlying type are incompatible. Meters cannot be confused with Kilometers, even though both are Doubles.

object Units:
  opaque type Meters = Double
  opaque type Kilometers = Double
  def m(d: Double): Meters = d
  def km(d: Double): Kilometers = d
  def toMeters(k: Kilometers): Meters = k * 1000

object Main:
  def main(args: Array[String]): Unit =
    val far = Units.km(2.0)
    println(Units.toMeters(far))

Opaque Types with Bounds

You can give an opaque type an upper bound with <:. This exposes part of the public API while keeping the exact representation hidden.

object Ids:
  opaque type UserId <: Int = Int
  def apply(i: Int): UserId = i

object Main:
  def main(args: Array[String]): Unit =
    val id = Ids(99)
    println(id + 1)

Smart Construction with Validation

Combine opaque types with a validating factory to ensure values are always legal. The constructor stays private; only the validated factory is public.

object Ages:
  opaque type Age = Int
  def of(i: Int): Option[Age] =
    if i >= 0 && i < 150 then Some(i) else None
  extension (a: Age) def value: Int = a

object Main:
  def main(args: Array[String]): Unit =
    println(Ages.of(30).map(_.value))
    println(Ages.of(-1))

Zero Runtime Cost

Unlike a value class or case class wrapper, an opaque type creates no object at runtime. A List of Meters is literally a list of doubles in the JVM, so there is no boxing.

object Temps:
  opaque type Celsius = Double
  def c(d: Double): Celsius = d
  extension (t: Celsius) def value: Double = t

object Main:
  def main(args: Array[String]): Unit =
    val readings = List(Temps.c(20.0), Temps.c(21.5))
    println(readings.map(_.value).sum)

Opaque vs Type Alias

A plain type X = Y alias is transparent: X and Y are fully interchangeable everywhere. An opaque type hides that equality outside its scope, giving real type safety.

object Demo:
  type Name = String          // transparent alias
  opaque type Email = String  // opaque
  def email(s: String): Email = s
  extension (e: Email) def raw: String = e

object Main:
  def main(args: Array[String]): Unit =
    val e = Demo.email("a@b.com")
    println(e.raw)

Opaque vs Case Class Wrapper

A case class wrapper like case class Meters(value: Double) also adds type safety but allocates an object. Opaque types give the same safety with the performance of the raw type.

object Money:
  opaque type Cents = Long
  def cents(n: Long): Cents = n
  extension (c: Cents)
    def +(o: Cents): Cents = c + o
    def value: Long = c

object Main:
  def main(args: Array[String]): Unit =
    val total = Money.cents(150) + Money.cents(50)
    println(total.value)

When to Use Opaque Types

Use opaque types when you want strong domain typing in hot paths or large collections.

  • Distinguish quantities (Meters vs Seconds).
  • Enforce validated invariants.
  • Avoid allocation overhead of wrappers.
object Geo:
  opaque type Latitude = Double
  def lat(d: Double): Option[Latitude] =
    if d >= -90 && d <= 90 then Some(d) else None
  extension (l: Latitude) def value: Double = l

object Main:
  def main(args: Array[String]): Unit =
    println(Geo.lat(41.0).map(_.value))

Quick Check

Test your understanding of opaque types.

Recap

You learned Scala 3 opaque types.

  • opaque type X = Y hides the equality outside its scope.
  • Provide factories and extension methods for the API.
  • Different opaque types over the same base are incompatible.
  • Optional upper bounds with <: expose partial API.
  • Zero runtime cost, unlike case class wrappers.
object Ids:
  opaque type OrderId = String
  def apply(s: String): OrderId = s
  extension (o: OrderId) def raw: String = o

object Main:
  def main(args: Array[String]): Unit =
    val id = Ids("ORD-1")
    println(id.raw)

Perguntas Frequentes

A aula “Tipos opacos” é grátis?

Sim — o texto completo de “Tipos opacos” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Scala for Backend Engineering & Functional Programming, atualize para CoddyKit PRO. O curso de Scala for Backend Engineering & Functional Programming inclui 4 aulas no total.

O que vou aprender em “Tipos opacos”?

Abstrações sem custo Você pratica Scala for Backend Engineering & Functional Programming com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Scala for Backend Engineering & Functional Programming?

Nenhuma experiência prévia é necessária. Scala for Backend Engineering & Functional Programming no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Tipos opacos”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Scala for Backend Engineering & Functional Programming?

Sim. Cada aula de Scala for Backend Engineering & Functional Programming inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Indentação significativa
  2. Enums no Scala 3
  3. Tipos opacos
  4. Tipos união e interseção
← Voltar para Scala for Backend Engineering & Functional Programming