Introducción: cuando la universalidad es enemiga
\n\nImagine que escribe código para configuración de red, análisis de registros o descripción de reglas de negocio. Cada vez: construcciones repetitivas, montones de paréntesis, condicionales anidados y una lectura tortuosa del código ajeno. ¿Le suena familiar?
\n\nEn estas situaciones, los lenguajes específicos del dominio (DSL) vienen al rescate. No es magia ni reinventar la rueda: es una forma de hacer el código tan legible que se documenta a sí mismo. En este artículo, analizaremos cómo crear su propio DSL en Swift: potente, elegante y que no requiere compilador.
\n\nUn DSL en Swift no es solo una tendencia moderna del desarrollo iOS. Es una herramienta utilizada en SwiftUI, Combine e incluso en bibliotecas de backend. Aprenderá a diseñar lenguajes que resuelven tareas específicas: desde el análisis de datos hasta la descripción de interfaces de usuario. ¡Allá vamos!
\n\n¿Qué es un DSL y por qué lo necesita un desarrollador?
\n\nDomain-Specific Language es un lenguaje diseñado para un área específica. A diferencia de un lenguaje de propósito general (como Swift o Python), un DSL habla el lenguaje de su dominio. Por ejemplo, en lugar de:
\n\nlet config = NetworkConfig()\nconfig.host = "api.example.com"\nconfig.port = 443\nconfig.timeout = 30\n\nUsted escribe:
\n\nlet config = networkConfig {\n host("api.example.com")\n port(443)\n timeout(30)\n}\n\nLa segunda opción se lee como una instrucción en lenguaje humano. Ese es el objetivo de un DSL: reducir la carga cognitiva y hacer que el código sea autodocumentado.
\n\nEn Swift, para crear DSL se utilizan principalmente:
\n- \n
- Result Builders — una nueva característica de Swift 5.4+ que permite construir estructuras anidadas mediante sintaxis declarativa. \n
- Cadenas funcionales — el patrón builder clásico. \n
- Operadores — sobrecarga de operadores para crear interfaces fluidas. \n
Pero vayamos al grano. Crearemos un DSL para analizar archivos CSV, una tarea que atormenta a todo desarrollador al menos una vez en la vida.
\n\nEjemplo 1: DSL simple para configuración de red
\n\nEmpecemos con un ejemplo básico que muestre la mecánica. Supongamos que necesitamos describir solicitudes HTTP. En lugar de montones de parámetros, un DSL conciso.
\n\n// Definimos la estructura de la solicitud\nstruct HTTPRequest {\n var method: String = "GET"\n var path: String = "/"\n var headers: [String: String] = [:]\n var body: Data? = nil\n}\n\n// Creamos el DSL mediante 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 solicitud\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// Funciones 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 es la base de cualquier DSL en Swift. @resultBuilder recoge todas las expresiones dentro del bloque y las transforma en una única estructura. Importante: cada elemento es un modificador que cambia el estado.
Ahora compliquemos la tarea: añadamos anidamiento y condiciones.