Introdução: quando a universalidade é inimiga
\n\nImagine que você está escrevendo código para configuração de rede, parsing de logs ou descrição de regras de negócio. Toda vez — construções repetitivas, montes de parênteses, operadores condicionais aninhados e uma leitura dolorosa do código alheio. Familiar?
\n\nNessas situações, as linguagens específicas de domínio (DSL) vêm em socorro. Não é mágica nem reinventar a roda — é uma forma de tornar o código tão legível que ele se documenta por si só. Neste artigo, vamos explorar como criar seu próprio DSL em Swift — poderoso, elegante e que não requer um compilador.
\n\nDSL em Swift não é apenas uma tendência moderna do desenvolvimento iOS. É uma ferramenta usada em SwiftUI, Combine e até em bibliotecas de backend. Você aprenderá a projetar linguagens que resolvem tarefas específicas: desde parsing de dados até descrição de UI. Vamos nessa!
\n\nO que é DSL e por que um desenvolvedor precisa dela?
\n\nDomain-Specific Language é uma linguagem voltada para uma área restrita. Diferente de uma linguagem geral (como Swift ou Python), a DSL fala a linguagem do seu domínio. Por exemplo, em vez de:
\n\nlet config = NetworkConfig()\nconfig.host = "api.example.com"\nconfig.port = 443\nconfig.timeout = 30\n\nVocê escreve:
\n\nlet config = networkConfig {\n host("api.example.com")\n port(443)\n timeout(30)\n}\n\nA segunda opção é lida como uma instrução em linguagem humana. Esse é o objetivo da DSL — reduzir a carga cognitiva e tornar o código autodocumentável.
\n\nEm Swift, para criar DSLs, os recursos mais usados são:
\n- \n
- Result Builders — novo recurso do Swift 5.4+, que permite construir estruturas aninhadas com sintaxe declarativa. \n
- Cadeias funcionais — o clássico padrão builder. \n
- Operadores — sobrecarga de operadores para criar interfaces fluidas. \n
Mas vamos direto ao ponto. Criaremos um DSL para parsing de arquivos CSV — uma tarefa que atormenta todo desenvolvedor pelo menos uma vez na vida.
\n\nExemplo 1: DSL simples para configuração de rede
\n\nVamos começar com um exemplo básico que mostra a mecânica. Suponha que precisamos descrever requisições HTTP. Em vez de uma pilha de parâmetros — um DSL conciso.
\n\n// Definindo a estrutura da requisição\nstruct HTTPRequest {\n var method: String = "GET"\n var path: String = "/"\n var headers: [String: String] = [:]\n var body: Data? = nil\n}\n\n// Criando DSL com Result Builder\n@resultBuilder\nstruct RequestBuilder {\n static func buildBlock(_ components: HTTPRequestModifier...) -> HTTPRequest {\n var request = HTTPRequest()\n for mod in components {\n mod.apply(&request)\n }\n return request\n }\n}\n\n// Modificadores de requisição\nprotocol HTTPRequestModifier {\n func apply(_ request: inout HTTPRequest)\n}\n\nstruct MethodModifier: HTTPRequestModifier {\n let method: String\n func apply(_ request: inout HTTPRequest) {\n request.method = method\n }\n}\n\nstruct PathModifier: HTTPRequestModifier {\n let path: String\n func apply(_ request: inout HTTPRequest) {\n request.path = path\n }\n}\n\n// Funções auxiliares\nfunc method(_ m: String) -> HTTPRequestModifier { MethodModifier(method: m) }\nfunc path(_ p: String) -> HTTPRequestModifier { PathModifier(path: p) }\n\n// Uso\n@RequestBuilder\nfunc buildGetRequest() -> HTTPRequest {\n method("GET")\n path("/users")\n}\n\nlet request = buildGetRequest()\nprint(request.method, request.path) // GET /users\n\nEste código é a base de qualquer DSL em Swift. O @resultBuilder coleta todas as expressões dentro do bloco e as transforma em uma única estrutura. Importante: cada elemento é um modificador que altera o estado.
Agora vamos complicar: adicionar aninhamento e condições.