|
| 1 | + |
| 2 | + |
| 3 | +# Kumi |
| 4 | + |
| 5 | +[](https://github.com/amuta/kumi/actions) |
| 6 | +[](https://badge.fury.io/rb/kumi) |
| 7 | +[](https://opensource.org/licenses/MIT) |
| 8 | + |
| 9 | +**[Prueba la demostración interactiva →](https://kumi-play-web.fly.dev/)** |
| 10 | + |
| 11 | +--- |
| 12 | + |
| 13 | +## ¿Qué es Kumi? |
| 14 | + |
| 15 | +Kumi es un **DSL declarativo para lógica de cálculo** — reglas fiscales, precios, puntuaciones, proyecciones financieras — que se compila a Ruby y JavaScript planos. |
| 16 | + |
| 17 | +Declaras la forma de tus datos de entrada y los valores que deseas calcular. El compilador determina el orden de evaluación, verifica tipos, detecta condiciones imposibles y emite código sin dependencias para cada destino. |
| 18 | + |
| 19 | +```ruby |
| 20 | +schema do |
| 21 | + input do |
| 22 | + array :items do |
| 23 | + hash :item do |
| 24 | + integer :quantity |
| 25 | + decimal :unit_price |
| 26 | + end |
| 27 | + end |
| 28 | + end |
| 29 | + |
| 30 | + value :line_totals, input.items.item.quantity * input.items.item.unit_price |
| 31 | + value :subtotal, fn(:sum, line_totals) |
| 32 | +end |
| 33 | +``` |
| 34 | + |
| 35 | +Sin bucles, sin tuberías de iteración: `line_totals` se calcula por elemento porque ahí es donde viven los datos, y `fn(:sum, ...)` lo reduce de nuevo a un escalar. Esto funciona con matrices anidadas arbitrariamente. |
| 36 | + |
| 37 | +## ¿Por qué? |
| 38 | + |
| 39 | +- **Una única fuente de verdad, dos destinos.** El mismo esquema se compila a Ruby y JavaScript con semántica idéntica: escribe la lógica de precios una vez, ejecútala en tu backend y en la vista previa del navegador. |
| 40 | +- **Difusión según la forma de los datos.** Las operaciones se alinean automáticamente sobre matrices basándose en la estructura de entrada declarada, incluidos datos anidados e irregulares. |
| 41 | +- **Verificaciones estáticas en tiempo de compilación.** La verificación de tipos, la detección de ciclos de dependencias y la detección de restricciones insatisfechas ocurren cuando se define el esquema, no en producción. |
| 42 | +- **Código generado sencillo y predecible.** La salida es determinista, sin dependencias, código lineal con bucles explícitos. Lo que lees es lo que se ejecuta. |
| 43 | + |
| 44 | +## Casos de uso |
| 45 | + |
| 46 | +Motores fiscales, modelos de precios, proyecciones financieras, reglas de cumplimiento, suscripción de seguros, calculadoras de tarifas de envío — en cualquier lugar donde la lógica de cálculo deba ser correcta, auditable y coherente entre plataformas. |
| 47 | + |
| 48 | +--- |
| 49 | + |
| 50 | +**Estado**: experimental. La API pública puede cambiar. La tipificación y algunas verificaciones estáticas aún están en evolución. |
| 51 | + |
| 52 | +**Comentarios**: ¿tienes un caso de uso o encontraste algún problema? Abre un issue o contáctanos (andremuta+kumi@gmail.com). |
| 53 | + |
| 54 | +--- |
| 55 | + |
| 56 | +## Instalación |
| 57 | + |
| 58 | +```bash |
| 59 | +gem install kumi |
| 60 | +``` |
| 61 | + |
| 62 | +Requiere Ruby 3.1+. Dependencias en tiempo de ejecución: `mutex_m` y `zeitwerk` (incluidas a través de Rubygems). |
| 63 | + |
| 64 | +## Inicio rápido |
| 65 | + |
| 66 | +```ruby |
| 67 | +require 'kumi' |
| 68 | + |
| 69 | +module Double |
| 70 | + extend Kumi::Schema |
| 71 | + |
| 72 | + schema do |
| 73 | + input { integer :x } |
| 74 | + value :doubled, input.x * 2 |
| 75 | + end |
| 76 | +end |
| 77 | + |
| 78 | +# Execute in Ruby |
| 79 | +result = Double.from(x: 5) |
| 80 | +result[:doubled] # => 10 |
| 81 | + |
| 82 | +# or just call the method directly |
| 83 | +Double._doubled(x: 5) # => 10 |
| 84 | + |
| 85 | +# Export to JavaScript (same logic) |
| 86 | +Double.write_source("output.mjs", platform: :javascript) |
| 87 | +# ./output.mjs |
| 88 | +# export function _doubled(input) { |
| 89 | +# let t1 = input["x"]; |
| 90 | +# let t3 = t1 * 2; |
| 91 | +# return t3; |
| 92 | +# } |
| 93 | +``` |
| 94 | + |
| 95 | +También puedes anular la estrategia de compilación sin modificar el código estableciendo |
| 96 | +`KUMI_COMPILATION_MODE` en `jit` o `aot` (por ejemplo, `export KUMI_COMPILATION_MODE=aot`). |
| 97 | + |
| 98 | +## Composición de esquemas |
| 99 | + |
| 100 | +Los esquemas pueden importar otros esquemas — y el compilador **integra todo en línea en tiempo de compilación**. El código generado no tiene ninguna dependencia en tiempo de ejecución sobre el módulo importado; la lógica importada participa en la difusión y fusión de bucles como cualquier expresión escrita localmente. |
| 101 | + |
| 102 | +```ruby |
| 103 | +module TaxPolicy |
| 104 | + extend Kumi::Schema |
| 105 | + |
| 106 | + schema do |
| 107 | + input { decimal :amount } |
| 108 | + value :tax, input.amount * 0.15 |
| 109 | + end |
| 110 | +end |
| 111 | + |
| 112 | +module Order |
| 113 | + extend Kumi::Schema |
| 114 | + |
| 115 | + schema do |
| 116 | + import :tax, from: TaxPolicy |
| 117 | + |
| 118 | + input do |
| 119 | + array :items do |
| 120 | + hash :item do |
| 121 | + decimal :price |
| 122 | + end |
| 123 | + end |
| 124 | + end |
| 125 | + |
| 126 | + # TaxPolicy only knows a scalar :amount — passing a vectorized |
| 127 | + # argument applies it per item automatically. |
| 128 | + value :item_taxes, fn(:tax, amount: input.items.item.price) |
| 129 | + value :total_tax, fn(:sum, item_taxes) |
| 130 | + end |
| 131 | +end |
| 132 | + |
| 133 | +Order.from(items: [{ price: 100 }, { price: 200 }, { price: 300 }])[:total_tax] |
| 134 | +# => 90.0 |
| 135 | +``` |
| 136 | + |
| 137 | +El JavaScript generado para `total_tax` muestra lo que significa "integrar todo en línea": la regla fiscal importada **y** la reducción `sum` se fusionan en un solo bucle, sin llamadas a `TaxPolicy`, sin matrices intermedias y sin runtime que empaquetar: |
| 138 | + |
| 139 | +```js |
| 140 | +export function _total_tax(input) { |
| 141 | + let t13 = input["items"]; |
| 142 | + let acc18 = 0.0; |
| 143 | + for (let items_i15 = 0; items_i15 < t13.length; items_i15++) { |
| 144 | + let items_el14 = t13[items_i15]; |
| 145 | + let t16 = items_el14["price"]; |
| 146 | + let t17 = 0.15; |
| 147 | + let t12 = t16 * t17; |
| 148 | + acc18 += t12; |
| 149 | + } |
| 150 | + let t6 = acc18; |
| 151 | + return t6; |
| 152 | +} |
| 153 | +``` |
| 154 | + |
| 155 | +Consulta [Importación de esquemas](docs/SCHEMA_IMPORTS.md) para entradas renombradas, múltiples importaciones e importaciones sobre matrices anidadas. |
| 156 | + |
| 157 | +## Rendimiento |
| 158 | + |
| 159 | +Los esquemas compilados son código lineal con bucles fusionados — no hay intérprete, motor de reglas ni llamadas a bibliotecas en el artefacto, por lo que el costo en tiempo de ejecución es lo que el lenguaje de hosting cobre por un bucle `for`. |
| 160 | + |
| 161 | +Dos ejemplos en el playground hacen esto tangible: |
| 162 | + |
| 163 | +- **[Nómina a gran escala](https://kumi-play-web.fly.dev/?example=payroll-at-scale)** — una ejecución completa de nómina (división de horas extras, bandas de retención progresiva, diez agregados) sobre **10,000 empleados en ~0,2 ms por llamada** en Chrome de escritorio. Presiona **Benchmark ×100** en la pestaña Ejecutar para medir la mediana / p95 en tu propia máquina. |
| 164 | +- **[Game of Life XL](https://kumi-play-web.fly.dev/?example=life-xl)** — reglas de Conway sobre una **cuadrícula de 83,000 celdas**, avanzando en vivo en un worker con las nueve lecturas de vecinos fusionadas en un solo pase — millones de actualizaciones de celdas por segundo, renderizado en tiempo real con un contador en vivo. |
| 165 | + |
| 166 | +## Ejemplos |
| 167 | + |
| 168 | +- **Calculadora de impuestos de EE. UU. (2024)** — un solo esquema calcula impuestos federales, estatales y FICA en múltiples estados de declaración. [Abrir en la demostración](https://kumi-play-web.fly.dev/?example=us-federal-tax-2024). |
| 169 | +- **Nómina a gran escala** — horas extras, bandas de retención y agregados sobre 10,000 empleados con medición de tiempo por ejecución. [Abrir en la demostración](https://kumi-play-web.fly.dev/?example=payroll-at-scale). |
| 170 | +- **Portafolio Monte Carlo** — simulaciones probabilísticas y visualizaciones tabulares. [Abrir en la demostración](https://kumi-play-web.fly.dev/?example=monte-carlo-simulation). |
| 171 | +- **Juego de la vida de Conway** — operaciones de matrices que impulsan una simulación basada en cuadrícula (la [versión XL](https://kumi-play-web.fly.dev/?example=life-xl) ejecuta 83k celdas en vivo). [Abrir en la demostración](https://kumi-play-web.fly.dev/?example=game-of-life). |
| 172 | + |
| 173 | +El selector de ejemplos del playground agrupa estos por tema (recorrido del lenguaje, lógica empresarial, escala y velocidad, simulaciones) — la [Introducción al lenguaje](https://kumi-play-web.fly.dev/?example=language-intro) es el mejor punto de partida. |
| 174 | + |
| 175 | +--- |
| 176 | + |
| 177 | +## Documentación |
| 178 | + |
| 179 | +- **[Referencia de sintaxis](docs/SYNTAX.md)** — sintaxis del DSL, tipos, operadores, funciones |
| 180 | +- **[Notas de sintaxis](docs/SYNTAX_NOTES.md)** — diferencias del analizador, recetas de entradas anidadas, literales de expresión y errores posteriores al análisis |
| 181 | +- **[Formas de entrada](docs/INPUTS.md)** — declarar escalares, matrices, hashes y la regla de elemento |
| 182 | +- **[Referencia de funciones](docs/FUNCTIONS.md)** — documentación generada automáticamente para todas las funciones y kernels ([JSON legible por máquina](docs/functions-reference.json)) |
| 183 | +- **[Semántica entre destinos](docs/CROSS_TARGET_SEMANTICS.md)** — dónde Ruby y JS divergirían (`to_string` de flotantes, conversiones de cadena, `pow`) y cómo los kernels mantienen la identidad |
| 184 | +- **[Importación de esquemas](docs/SCHEMA_IMPORTS.md)** — composición y reutilización de esquemas |
| 185 | +- **[Arquitectura](docs/ARCHITECTURE.md)** — la tubería del compilador y la pila IR |
| 186 | +- **[Pruebas golden](docs/GOLDEN_TESTS.md)** — el entorno de pruebas de extremo a extremo |
| 187 | +- **[Guía de desarrollo](docs/DEVELOPMENT.md)** — herramientas, generación de documentación, integración con IDE |
| 188 | + |
| 189 | +--- |
| 190 | + |
| 191 | +## Licencia |
| 192 | + |
| 193 | +Licencia MIT. Consulta [LICENSE](LICENSE). |
0 commit comments