Modelagem com ADTs
Torne estados ilegais irrepresentáveis
Modelagem com ADTs é uma aula grátis de Scala for Backend Engineering & Functional Programming no CoddyKit. Esta é a aula 1 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.
Tipos algébricos de dados
Os tipos algébricos de dados (ADT) são a base da modelagem funcional de domínios. Eles combinam tipos produto (AND) e tipos soma (OR) para descrever dados com precisão.
- Produto: um registro com vários campos.
- Soma: uma escolha entre várias variantes.
case class Point(x: Int, y: Int) // product type
object Main:
def main(args: Array[String]): Unit =
println(Point(1, 2))Tipos produto
Um tipo produto agrupa valores. Na Scala, uma case class é um produto: uma instância contém todos os seus campos ao mesmo tempo.
case class User(name: String, age: Int)
object Main:
def main(args: Array[String]): Unit =
val u = User("Ada", 36)
println(u.name)
println(u.age)Tipos soma
Um tipo soma é um valor que corresponde exatamente a uma entre várias variantes. Os enums da Scala 3 expressam isso diretamente.
enum PaymentMethod:
case Cash
case Card(number: String)
case Crypto(wallet: String)
object Main:
def main(args: Array[String]): Unit =
val p: PaymentMethod = PaymentMethod.Card("1234")
println(p)Tornando estados ilegais irrepresentáveis
O princípio central é projetar tipos de modo que dados inválidos nem sequer possam ser construídos. Se o sistema de tipos proíbe um estado incorreto, você nunca precisa verificá-lo em tempo de execução.
enum Connection:
case Disconnected
case Connected(sessionId: String)
object Main:
def main(args: Array[String]): Unit =
// No way to have a sessionId while Disconnected
val c: Connection = Connection.Connected("abc")
println(c)Projeto ruim versus projeto bom
Um registro plano com campos anuláveis favorece estados ilegais. Modelar cada caso como uma variante elimina esses estados.
- Ruim:
case class Conn(connected: Boolean, sessionId: String)permite connected=false com um sessionId. - Bom: um tipo soma associa o campo ao estado correto.
enum Door:
case Open
case Closed
case Locked(key: String)
object Main:
def main(args: Array[String]): Unit =
val d: Door = Door.Locked("k1")
println(d)Combinando produtos e somas
Modelos reais aninham produtos dentro de somas e vice-versa. Cada variante pode conter seu próprio produto de campos.
case class Address(city: String, zip: String)
enum Contact:
case Email(value: String)
case Postal(address: Address)
object Main:
def main(args: Array[String]): Unit =
val c: Contact = Contact.Postal(Address("Paris", "75001"))
println(c)Correspondência de padrões com ADT
Você utiliza ADT com correspondência de padrões. Como o tipo é fechado, o compilador avisa se alguma variante não for tratada.
enum Shape:
case Circle(r: Double)
case Rect(w: Double, h: Double)
object Main:
def area(s: Shape): Double = s match
case Shape.Circle(r) => 3.14159 * r * r
case Shape.Rect(w, h) => w * h
def main(args: Array[String]): Unit =
println(area(Shape.Rect(2, 3)))ADT recursivos
Os ADT podem referenciar a si mesmos, modelando árvores, listas e expressões naturalmente.
enum Expr:
case Num(value: Int)
case Add(left: Expr, right: Expr)
object Main:
def eval(e: Expr): Int = e match
case Expr.Num(v) => v
case Expr.Add(l, r) => eval(l) + eval(r)
def main(args: Array[String]): Unit =
val e = Expr.Add(Expr.Num(2), Expr.Num(3))
println(eval(e))Opcionalidade com Option
Use Option em vez de null para modelar um campo que pode estar ausente. O tipo torna a ausência explícita e obriga seu tratamento.
case class Profile(name: String, nickname: Option[String])
object Main:
def main(args: Array[String]): Unit =
val p = Profile("Grace", None)
println(p.nickname.getOrElse("(none)"))Modelando quantidades com precisão
Substitua a obsessão por tipos primitivos. Em vez de Ints e Strings brutos, envolva quantidades com significado para que os tipos documentem a intenção e evitem confusões.
case class Quantity(value: Int)
case class Price(cents: Long)
case class LineItem(qty: Quantity, price: Price)
object Main:
def main(args: Array[String]): Unit =
val item = LineItem(Quantity(3), Price(500))
println(item)Por que os ADT são importantes
Os ADT proporcionam correção e clareza.
- Não é possível construir estados inválidos.
- A correspondência exaustiva detecta casos não tratados em tempo de compilação.
- A forma dos dados documenta o domínio.
enum OrderStatus:
case Pending
case Shipped(tracking: String)
case Delivered(at: Long)
object Main:
def main(args: Array[String]): Unit =
val s: OrderStatus = OrderStatus.Shipped("TRK1")
println(s)Verificação rápida
Teste sua compreensão da modelagem baseada em ADT.
Recapitulação
Você aprendeu a modelar domínios com ADT.
- Os tipos produto (
case class) combinam campos com AND. - Os tipos soma (
enum) oferecem uma escolha com OR. - Aninhe-os para modelar domínios ricos e estruturas recursivas.
- Use
Optionem vez de null. - Projete os tipos para que estados ilegais não possam ser representados.
enum Event:
case Created(id: Int)
case Deleted(id: Int, reason: String)
object Main:
def describe(e: Event): String = e match
case Event.Created(id) => s"created $id"
case Event.Deleted(id, reason) => s"deleted $id: $reason"
def main(args: Array[String]): Unit =
println(describe(Event.Deleted(1, "spam")))Perguntas Frequentes
A aula “Modelagem com ADTs” é grátis?
Sim — o texto completo de “Modelagem com ADTs” é 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 “Modelagem com ADTs”?
Torne estados ilegais irrepresentáveis 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 1 de 4.
Quanto tempo leva a aula “Modelagem com ADTs”?
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
- Modelagem com ADTs
- Construtores inteligentes
- Newtypes
- Composição de domínios