O “Problema da Expressão” (Expression Problem) é um desafio clássico no design de linguagens de programação. Neste post, vamos explorar como abordá-lo em Go usando generics, fazendo referência às ideias apresentadas no artigo Featherweight Go e examinando as limitações da implementação atual do Go.
Para entender o desafio, vale observar como o artigo Featherweight Go o define:
“O objetivo é definir um tipo de dado por casos, onde seja possível adicionar novos casos ao tipo de dado e novas funções sobre esse tipo de dado, sem recompilar o código existente e mantendo a segurança estática de tipos.”
O artigo traz uma excelente analogia para ajudar a visualizar isso:
“A capacidade de uma linguagem de resolver o Problema da Expressão é um indicador marcante de sua capacidade de expressão. Podemos pensar nos casos como linhas e nas funções como colunas em uma tabela. Em uma linguagem funcional, as linhas são fixas (casos na declaração de um tipo de dado), mas é fácil adicionar novas colunas (funções). Em uma linguagem orientada a objetos, as colunas são fixas (métodos na declaração de uma classe), mas é fácil adicionar novas linhas (subclasses). Queremos facilitar a adição tanto de linhas quanto de colunas.”
Em resumo: uma solução verdadeira nos permite adicionar facilmente novas variantes de dados (linhas) e novas operações/comportamentos (colunas) ao nosso código sem precisar modificar o código-fonte existente.
A solução proposta no rascunho original do Featherweight Go depende de um recurso que acabou ficando de fora do lançamento final de generics no Go 1.18.
Especificamente, a solução ideal do artigo pressupõe que você possa aplicar restrições de tipo (type constraints) mais estritas no receiver de um método do que aquelas definidas na struct base. Por exemplo, idealmente gostaríamos de fazer o seguinte:
// Hypothetical (Not valid Go):
// Constraining the method receiver directly
func (e Plus[T Evaler]) Eval() int
func (e Plus[T Stringer]) String() string
No entanto, a implementação de generics em Go exige que as restrições de tipo do receiver do método correspondam perfeitamente à definição de tipo da struct. Não é possível adicionar novos requisitos de restrição (como Evaler ou Stringer) método a método.
Como não podemos restringir parâmetros de tipo no nível do método, somos forçados a restringi-los no nível da struct.
Abaixo está um exemplo de código adaptado que funciona perfeitamente com generics no Go 1.18+. Porém, repare que somos obrigados a definir antecipadamente uma interface combinada Expr. Como precisamos modificar essa interface toda vez que quisermos adicionar uma nova operação, esta não é uma solução verdadeira para o Problema da Expressão, mas sim uma solução prática alternativa adaptada do artigo:
package main
import (
"fmt"
)
// 1. Define our Base Data Type
type Num struct {
value int
}
// 2. Define our Operations (Columns)
type Evaler interface {
Eval() int
}
type Stringer interface {
String() string
}
// Implement the operations for our base type
func (e Num) Eval() int {
return e.value
}
func (e Num) String() string {
return fmt.Sprintf("%d", e.value)
}
// 3. The Compromise: We must pre-define a combined interface.
// If we want to add a new operation later, we are forced to edit this interface,
// violating the core rule of the Expression Problem.
type Expr interface {
Evaler
Stringer
}
// 4. Define a Generic Data Type (Row)
// Because Go doesn't allow method-level constraints, we must constrain T to Expr here.
type Plus[T Expr] struct {
left T
right T
}
func (e Plus[T]) Eval() int {
return e.left.Eval() + e.right.Eval()
}
func (e Plus[T]) String() string {
return fmt.Sprintf("(%s+%s)", e.left.String(), e.right.String())
}
// 5. Execution
func main() {
var e Expr = Plus[Expr]{left: Num{1}, right: Num{2}}
fmt.Println(e.Eval()) // Output: 3
fmt.Println(e.String()) // Output: (1+2)
}
Embora os generics do Go sejam incrivelmente poderosos e tragam uma flexibilidade muito necessária para a linguagem, eles não resolvem nativamente o Problema da Expressão da maneira que o artigo Featherweight Go imaginou originalmente. Ao exigir que as restrições sejam definidas no nível da struct em vez do nível do método, os desenvolvedores ainda precisam planejar interfaces com antecedência ou modificar o código existente ao adicionar novas operações a tipos genéricos.
Baixe a edição offline selecionada de O Estilo de Vida do Kaue—um guia prático para otimizar 🍏 Saúde, 💰 Riqueza, ❤️ Amor e 🧘 Sentido.
Receba notificações quando eu publicar novos ensaios, resumos de livros e guias práticos sobre como viver bem. Sem spam—cancele quando quiser.
Tem alguma pergunta, perspectiva ou sugestão? Compartilhe suas ideias abaixo. Os comentários usam o GitHub Discussions (sem anúncios e sem spam)—clique em Entrar com o GitHub abaixo para comentar ou reagir (criar uma conta gratuita no GitHub leva menos de um minuto).
Atualizado em 11 de out. de 2026.
AVISO LEGAL: O conteúdo deste site tem finalidade exclusivamente informativa e educacional e não constitui aconselhamento médico, psicológico, financeiro, de investimentos, tributário ou jurídico. As ideias, a curadoria e as opiniões aqui apresentadas são exclusivamente minhas e não representam as do meu empregador. Os resumos de livros, palestras e artigos são comentários educacionais independentes e permanecem propriedade intelectual de seus respectivos autores. Ferramentas de IA podem ser utilizadas para auxiliar na edição, formatação e síntese; embora todos os esforços sejam feitos para garantir a precisão, erros podem ocorrer. PRIVACIDADE: Este site utiliza serviços de terceiros para inscrição opcional em newsletter (follow.it), comentários (giscus / GitHub Discussions) e mídia incorporada (YouTube), que podem processar endereços IP e cookies dos visitantes de acordo com suas respectivas políticas de privacidade.