0Pricing
Scala for Backend Engineering & Functional Programming · Aula

O padrão de classe de tipos

Polimorfismo ad hoc.

O padrão de classe de tipos é 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.

O que é uma classe de tipos?

Uma classe de tipos é um padrão para adicionar comportamento a tipos sem modificá-los. É uma forma de polimorfismo ad hoc: a mesma operação funciona para vários tipos não relacionados, escolhida pelo compilador com base no tipo.

Esse conceito vem de Haskell e é amplamente usado em bibliotecas Scala, como Cats.

O problema que ela resolve

A herança força a inclusão do comportamento em um tipo no momento de sua definição. Porém, muitas vezes você não pode editar um tipo (porque ele está em uma biblioteca) ou quer comportamentos diferentes em contextos distintos. As classes de tipos permitem associar comportamento externamente.

Etapa 1: definir o trait

Uma classe de tipos é um trait parametrizado por um tipo. Aqui, Show[A] descreve como renderizar qualquer A como uma String.

trait Show[A] {
  def show(value: A): String
}

@main def run(): Unit = {
  println("Show trait defined")
}

Etapa 2: fornecer instâncias

Para cada tipo concreto que você deseja aceitar, crie uma instance do trait. Essas são as instâncias da classe de tipos.

trait Show[A] { def show(value: A): String }

object Main {
  val intShow: Show[Int] = (v: Int) => s"Int($v)"
  val strShow: Show[String] = (v: String) => s"Str($v)"

  def main(args: Array[String]): Unit = {
    println(intShow.show(7))
    println(strShow.show("hi"))
  }
}

Etapa 3: usar a instância

Uma função recebe a instância como parâmetro. A mesma função render funciona para qualquer tipo que tenha uma instância de Show.

trait Show[A] { def show(value: A): String }

object Main {
  def render[A](value: A, s: Show[A]): String = s.show(value)

  val intShow: Show[Int] = (v: Int) => s"<$v>"

  def main(args: Array[String]): Unit = {
    println(render(99, intShow))
  }
}

Tornando-a implícita

Passar instâncias manualmente é trabalhoso. Marcar a instância como implicit e o parâmetro como implicit (ou using no Scala 3) permite que o compilador a forneça automaticamente.

trait Show[A] { def show(value: A): String }

object Main {
  implicit val intShow: Show[Int] = (v: Int) => s"<$v>"

  def render[A](value: A)(implicit s: Show[A]): String = s.show(value)

  def main(args: Array[String]): Unit = {
    println(render(42))
  }
}

Polimorfismo ad hoc

O mesmo nome de função direciona a execução para implementações diferentes com base no tipo do argumento. Isso é polimorfismo ad hoc, distinto do polimorfismo por subtipos (herança) e do polimorfismo paramétrico (genéricos).

trait Show[A] { def show(value: A): String }

object Main {
  implicit val intShow: Show[Int] = (v: Int) => s"int:$v"
  implicit val boolShow: Show[Boolean] = (v: Boolean) => s"bool:$v"

  def render[A](value: A)(implicit s: Show[A]): String = s.show(value)

  def main(args: Array[String]): Unit = {
    println(render(5))
    println(render(true))
  }
}

Trabalhando com tipos personalizados

O verdadeiro poder está em adicionar comportamento aos seus próprios tipos de forma organizada, mantendo a classe de dados livre de preocupações de formatação.

trait Show[A] { def show(value: A): String }

case class User(name: String, age: Int)

object Main {
  implicit val userShow: Show[User] = (u: User) => s"${u.name} (${u.age})"

  def render[A](value: A)(implicit s: Show[A]): String = s.show(value)

  def main(args: Array[String]): Unit = {
    println(render(User("Ada", 36)))
  }
}

Classes de tipos versus interfaces

Uma interface acopla o comportamento à definição do tipo; uma classe de tipos os desacopla.

  • Você pode adicionar uma instância de classe de tipos a um tipo que não pertence a você.
  • Você pode ter várias instâncias para contextos diferentes.
  • O comportamento é selecionado pelo compilador no local da chamada.

Três componentes

Toda classe de tipos tem três partes:

  • O trait (a operação abstrata).
  • As instâncias (implementações para cada tipo).
  • A interface (funções que exigem uma instância).

Você explorará cada uma delas nas próximas lições.

Um mini exemplo completo

Reunindo o padrão em uma função genérica que usa uma instância implícita.

trait Show[A] { def show(value: A): String }

object Main {
  implicit val intShow: Show[Int] = (v: Int) => s"#$v"
  implicit val strShow: Show[String] = (v: String) => '"' + v + '"'

  def printAll[A](xs: List[A])(implicit s: Show[A]): Unit =
    xs.foreach(x => println(s.show(x)))

  def main(args: Array[String]): Unit = {
    printAll(List(1, 2, 3))
    printAll(List("a", "b"))
  }
}

Verificação rápida

Teste sua compreensão do padrão de classe de tipos.

Recapitulação

Você aprendeu o padrão de classe de tipos:

  • Um trait parametrizado descreve uma operação.
  • Instâncias a implementam para cada tipo.
  • Parâmetros implicit permitem que o compilador forneça a instância.
  • Isso proporciona polimorfismo ad hoc e permite estender tipos que não pertencem a você.

Perguntas Frequentes

A aula “O padrão de classe de tipos” é grátis?

Sim — o texto completo de “O padrão de classe de tipos” é 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 “O padrão de classe de tipos”?

Polimorfismo ad hoc. 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 “O padrão de classe de tipos”?

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. O padrão de classe de tipos
  2. Definindo instâncias
  3. Classes de tipos comuns
  4. Derivação de classes de tipos
← Voltar para Scala for Backend Engineering & Functional Programming