DSL em Go: transformando seu código em uma nova linguagem
Imagine que você está escrevendo um sistema de processamento de pedidos. Todos os dias, os gerentes pedem: "Adicione um desconto de 10% para todos os clientes de Moscou, mas apenas se o valor do pedido for maior que 5000". Você mexe no código, ajusta if-else, testa, faz deploy. Uma semana depois, um novo pedido: "Desconto de 15% para VIP, se houver mais de três itens". Familiar?
E se os próprios gerentes pudessem escrever essas regras? Sem a sua participação. Sem risco de quebrar a produção. É para isso que existem as linguagens específicas de domínio (DSL) — linguagens compactas, focadas em uma tarefa específica. E o Go, com sua simplicidade e desempenho, é ideal para criar essas DSLs.
Neste artigo, vamos explorar como criar DSLs em Go — desde soluções embutidas simples até parsers externos completos. Você aprenderá como automatizar tarefas rotineiras e dar aos usuários uma ferramenta que eles entenderão sem programação.
O que é DSL e por que usá-la em Go?
DSL (Domain-Specific Language) é uma linguagem de programação criada para uma área restrita. Ao contrário das linguagens de uso geral (Go, Python, Java), a DSL resolve uma única tarefa, mas o faz de forma elegante.
Exemplos da vida real:
- SQL — DSL para trabalhar com bancos de dados.
- Regex — DSL para buscar padrões em texto.
- HTML — DSL para marcação de páginas web.
Em Go, as DSLs são usadas para:
- Configuração — em vez de YAML/JSON, você pode escrever lógica em sua própria linguagem.
- Regras de negócio — permitir que analistas alterem a lógica sem programadores.
- Testes — descrever cenários de forma legível.
- Processamento de dados — criar pipelines de transformação.
Go não foi escolhido por acaso: seu compilador é rápido, a sintaxe é concisa e as ferramentas integradas (como text/scanner ou go/ast) simplificam o parsing. Mas o principal é que Go permite criar tanto DSLs internas (embutidas na sintaxe do próprio Go) quanto DSLs externas (com sintaxe e parser próprios).
DSLs internas em Go: usando a sintaxe da linguagem
DSLs internas são bibliotecas que parecem uma linguagem separada, mas usam a sintaxe do Go. Elas não exigem a escrita de um parser: você simplesmente escreve funções que se "encaixam" em cadeias de chamadas.
Exemplo: DSL para regras de desconto
Suponha que queremos que as regras tenham esta aparência:
discount := Rule("Desconto VIP"). When(City("Moscou")). When(OrderSum > 5000). Apply(Discount(10))Como implementar isso em Go? Usamos o padrão Builder e funções de ordem superior:
package main
import "fmt"
// Rule - estrutura da regratype Rule struct { Name string Checks []func(order map[string]interface{}) bool Discount int}
// When adiciona uma condiçãofunc (r *Rule) When(check func(order map[string]interface{}) bool) *Rule { r.Checks = append(r.Checks, check) return r}
// Apply define o descontofunc (r *Rule) Apply(discount int) *Rule { r.Discount = discount return r}
// Evaluate verifica se o pedido se qualificafunc (r *Rule) Evaluate(order map[string]interface{}) bool { for _, check := range r.Checks { if !check(order) { return false } } return true}
// Funções auxiliares de condiçãofunc City(city string) func(map[string]interface{}) bool { return func(order map[string]interface{}) bool { return order["city"] == city }}
func OrderSum(order map[string]interface{}) int { return order["sum"].(int)}
// Sobrecarga do operador > via closurefunc GreaterThan(a func(map[string]interface{}) int, b