Back to flin
flin

El modelo temporal completo: lo que ningún otro lenguaje tiene

Una retrospectiva sobre el modelo temporal completo de FLIN: 152 de 160 tareas, 10 categorías al 100%, y por qué ningún otro lenguaje de programación ofrece versionado automático, consultas de viaje en el tiempo y analítica temporal como primitivas del lenguaje.

Thales & Claude | March 30, 2026 13 min flin
EN/ FR/ ES
flinrust

El 2 de enero de 2026, la Sesión 012 agregó la estructura EntityVersion a la máquina virtual de FLIN. Cincuenta minutos. Diez pruebas. El primer paso tentativo hacia un modelo de datos temporal.

Para el 7 de enero, Sesión 088, el modelo temporal alcanzaba ciento cincuenta y dos de ciento sesenta tareas completadas: noventa y cinco por ciento. Diez de once categorías al cien por ciento. Más de mil pruebas pasando. Un sistema completo y listo para producción para versionado automático, consultas de viaje en el tiempo, eliminación suave y permanente, restauración, filtrado temporal, analítica de comparación, metadatos de versión, aritmética temporal y almacenamiento bitemporal.

Este artículo es la retrospectiva. No el código, no los bugs, no los detalles de implementación: esos se cubren en los nueve artículos anteriores. Esto trata sobre lo que construimos, por qué importa y qué significa para el futuro del desarrollo de aplicaciones.

La puntuación final

CategoríaTareasEstado
TEMP-1: Eliminación suave básica5/5Completa
TEMP-2: Acceso Temporal (@)18/18Completa
TEMP-3: Palabras clave temporales14/14Completa
TEMP-4: Consultas de historial22/22Completa
TEMP-5: Aritmética temporal12/12Completa
TEMP-6: Comparaciones temporales10/10Completa
TEMP-7: Filtros basados en tiempo15/15Completa
TEMP-8: Eliminación permanente/Restauración12/12Completa
TEMP-9: Políticas de retención0/10Diferida
TEMP-10: Almacenamiento bitemporal20/20Completa
TEMP-11: Pruebas de integración27/27Completa
Total152/16095%

La única categoría incompleta es TEMP-9 (Políticas de retención): una funcionalidad de optimización para la limpieza automática de versiones antiguas. Las diez tareas restantes son "deseables" para despliegues en producción pero no afectan las capacidades temporales principales. Cada funcionalidad del modelo temporal está implementada, probada y funcionando.

Lo que incluye el modelo temporal de FLIN

Un inventario completo de las capacidades temporales, construidas a lo largo de veintiséis sesiones:

Versionado automático Cada entidad rastrea su historial completo automáticamente. Sin configuración, sin anotaciones, sin activación manual. Cada save crea una nueva versión inmutable con un número de versión secuencial y una marca temporal.

flinentity Product { name: text, price: number }

product = Product { name: "Widget", price: 10 }
save product          // Version 1
product.price = 15
save product          // Version 2
product.price = 20
save product          // Version 3

Consultas de viaje en el tiempo (operador @) Tres formas de acceso a un punto en el tiempo: números de versión relativos, cadenas de fecha absolutas y palabras clave temporales.

flinproduct @ -1                   // Previous version
product @ "2024-06-15"         // State on June 15, 2024
product @ yesterday            // State as of yesterday
product @ last_month           // State 30 days ago

Propiedad history Línea temporal completa de versiones para cualquier entidad, iterable en plantillas.

flin{for ver in product.history}
    <p>v{ver.version_number}: ${ver.price} at {ver.created_at}</p>
{/for}

Filtrado y ordenamiento temporal Operaciones estilo consulta sobre listas de historial.

flinexpensive = product.history.where_field("price", ">", 15)
chronological = product.history.order_by("created_at", "asc")
combined = product.history
    .where_field("price", ">=", 10)
    .order_by("price", "desc")

Eliminación suave, eliminación permanente y restauración Modelo de eliminación en tres niveles con cumplimiento GDPR incorporado.

flindelete product        // Soft: history preserved, restorable
restore(product)      // Undo soft delete
destroy product       // Hard: permanent erasure, GDPR compliant

Helpers de comparación temporal Seis funciones nativas para detección de cambios y analítica.

flinfield_changed(product, "price")              // true/false
calculate_delta(old_price, new_price)         // numeric difference
percent_change(old_price, new_price)          // percentage
changed_from(product, "price", 15.00)         // was it 15 before?
field_history(product, "price")               // [10, 15, 20]

Metadatos de versión Cada entidad expone metadatos del ciclo de vida como propiedades de primera clase.

flinproduct.id                // Entity identifier
product.version_number    // Current version
product.created_at        // Creation timestamp
product.updated_at        // Last modification
product.deleted_at        // Deletion timestamp (optional)

Aritmética temporal Literales de duración y operaciones temporales con tipado seguro.

flindeadline = now + 7.days
reminder = deadline - 1.hours
time_left = deadline - now
cache_ttl = 5.minutes

Almacenamiento bitemporal Cada versión tiene marcas temporales valid_from y valid_to, permitiendo consultas de rango eficientes y búsquedas por punto en el tiempo.

Persistencia del historial El historial de versiones persiste en disco y sobrevive a reinicios de la aplicación. El almacenamiento basado en WAL garantiza durabilidad sin sacrificar rendimiento de escritura.

Lo que ningún otro lenguaje tiene

Analizamos todos los lenguajes de programación y frameworks principales antes y durante el desarrollo de FLIN. Ninguno ofrece lo que el modelo temporal de FLIN proporciona como característica a nivel de lenguaje.

Bases de datos SQL: tablas temporales PostgreSQL, MariaDB y SQL Server soportan tablas temporales (también llamadas tablas versionadas por el sistema). Estas rastrean el historial de filas automáticamente, similar al enfoque de FLIN. Pero son una funcionalidad de la base de datos, no del lenguaje. El desarrollador debe:

  1. Crear la tabla temporal con sintaxis específica.
  2. Escribir consultas SQL con cláusulas FOR SYSTEM_TIME AS OF.
  3. Manejar las columnas temporales en el código de la aplicación.
  4. Construir componentes de UI para mostrar el historial.
  5. Implementar eliminación suave, eliminación permanente y restauración por separado.

FLIN integra las capacidades temporales en el lenguaje mismo. El operador @, la propiedad .history y los helpers de comparación no son extensiones SQL: son sintaxis nativa.

Frameworks de Event Sourcing El event sourcing (Axon, EventStore, Marten) almacena eventos y reconstruye el estado reproduciéndolos. Esto es potente para ciertos dominios pero fundamentalmente diferente del enfoque de FLIN:

  • El event sourcing requiere definir eventos, handlers, proyecciones y sagas. FLIN no requiere nada: el seguimiento temporal es automático.
  • Consultar un estado pasado en event sourcing significa reproducir eventos hasta ese punto. En FLIN, es una simple búsqueda indexada.
  • El event sourcing es un patrón, no una característica del lenguaje. Requiere adopción de framework y compromiso arquitectónico. El modelo temporal de FLIN funciona en cada entidad, siempre.

Sistemas de bases de datos inmutables Datomic y XTDB (anteriormente Crux) son bases de datos inmutables que almacenan todos los estados históricos. Son la coincidencia conceptual más cercana al modelo temporal de FLIN. Pero son sistemas de bases de datos, no lenguajes de programación. El desarrollador sigue escribiendo código de aplicación en Clojure (para Datomic) o cualquier lenguaje JVM (para XTDB), con consultas temporales expresadas como consultas de base de datos en lugar de expresiones del lenguaje.

FLIN convierte product @ yesterday en una expresión del lenguaje, verificada de tipos en tiempo de compilación, con el resultado utilizable directamente en plantillas y lógica de negocio. Sin frontera de lenguaje de consulta. Sin capa de traducción ORM.

Bibliotecas de auditoría Cada framework tiene gemas, paquetes y plugins de auditoría (PaperTrail para Rails, django-simple-history para Python, Javers para Java). Estas añaden versionado en la capa de aplicación. Son útiles pero limitadas:

  • Requieren configuración por modelo.
  • Almacenan diffs o snapshots en tablas separadas.
  • Proporcionan funcionalidad de auditoría de solo lectura, no viaje en el tiempo de propósito general.
  • No se integran con el sistema de tipos, el compilador o la capa de vistas.

El modelo temporal de FLIN no es un complemento. Es el modelo de datos.

El viaje en números

MétricaValor
Sesiones26 (012 a 088)
Días calendario6 (2 al 7 de enero de 2026)
Tareas completadas152 de 160
Categorías al 100%10 de 11
Pruebas de integración36 (todas pasando)
Pruebas de biblioteca1.011 (todas pasando)
Líneas de código temporal~2.500
Bugs encontrados y corregidos8 mayores, decenas menores
Nuevos opcodes de VM4 (AtVersion, AtTime, AtDate, History) + 2 (ListFilterField, ListOrderBy)
Nuevas funciones nativas6 (helpers de comparación)
Nuevos nodos AST2 (Expr::Temporal, Expr::Duration)
Nuevos tipos2 (FlinType::Duration, enum DurationUnit)

Seis días. Veintiséis sesiones. Un modelo de datos temporal completo tejido en cada capa de un lenguaje de programación, desde el lexer hasta la base de datos.

La arquitectura que lo hizo posible

La arquitectura por capas de FLIN -- lexer, parser, verificador de tipos, generador de código, VM, base de datos -- fue tanto el desafío como el habilitador del modelo temporal.

El desafío: las funcionalidades temporales requerían cambios en cada capa. El operador @ necesitaba un token en el lexer, una regla de parseo en el parser, lógica de verificación de tipos, emisión de bytecode, ejecución en la VM y consultas de base de datos. Un bug en cualquier capa producía síntomas confusos en otra.

El habilitador: la separación limpia entre capas significaba que cada pieza podía construirse y probarse independientemente. Los tokens del lexer se definieron antes que las reglas del parser. Las reglas del parser se definieron antes que la lógica del verificador de tipos. La lógica del verificador de tipos se definió antes que el generador de código. Este enfoque por capas permitió el desarrollo paralelo y el progreso incremental -- la Sesión 012 construyó la capa de VM mientras el parser y el verificador de tipos aún se estaban poniendo al día.

La arquitectura también significaba que las funcionalidades temporales se componían naturalmente con las funcionalidades existentes del lenguaje. El operador @ devuelve una entidad, así que funciona con acceso a campos, condicionales, bucles y llamadas a funciones. La propiedad .history devuelve una lista, así que funciona con .where_field(), .order_by(), .count y bucles {for}. Los literales de duración son enteros en tiempo de ejecución, así que funcionan con los opcodes aritméticos existentes.

Sin casos especiales. Sin "modo temporal". Solo expresiones que devuelven valores, compuestas a través de los mismos mecanismos que cualquier otra expresión en el lenguaje.

Lo que haríamos diferente

Empezar con pruebas de integración

El mayor consumo de tiempo fue la maratón de depuración de las Sesiones 068-076. Construimos funcionalidades temporales capa por capa, probamos cada capa aisladamente y asumimos que el flujo de extremo a extremo funcionaba. No fue así. El stub de AtTime fue el ejemplo más vergonzoso: una funcionalidad que compilaba, ejecutaba y devolvía un valor -- solo que el valor equivocado.

Si hubiéramos escrito pruebas de integración de extremo a extremo desde el principio -- código fuente FLIN de entrada, salida HTML de salida -- habríamos detectado el stub inmediatamente. El modelo temporal habría estado completo en la mitad de las sesiones.

Rastrear a nivel de tarea, no a nivel de categoría

Nuestro documento de seguimiento registraba porcentajes a nivel de categoría pero no la completitud de tareas individuales. Esto hacía imposible determinar si "TEMP-2 al 89%" significaba "dieciséis tareas hechas, dos restantes" o "la mayoría de las cosas funcionan pero no estamos seguros de cuáles dos no". El seguimiento a nivel de tarea habría prevenido el descubrimiento de seis tareas en la Sesión 079.

Definir la semántica antes de la implementación

El bug de duplicación de historial (Sesión 075) existía porque nunca definimos explícitamente quién era responsable de incluir la versión actual en el resultado de .history. ¿Era la base de datos? ¿La VM? ¿Ambas? La ambigüedad llevó a que ambas lo hicieran, causando duplicación.

Una especificación semántica de un párrafo -- "la base de datos almacena solo las versiones pasadas; la VM agrega la versión actual al construir los resultados de .history" -- habría prevenido el bug por completo.

El cinco por ciento restante

TEMP-9 (Políticas de retención) representa las diez tareas restantes. Son funcionalidades de optimización para despliegues en producción:

flin// Planned syntax
entity Metric {
    value: number

    @retention(90.days)          // Keep 90 days of history
}

entity Metric {
    value: number

    @compact(after: 30.days, to: "daily")  // Compact old data
}

Las políticas de retención podan automáticamente las versiones antiguas para gestionar el crecimiento del almacenamiento. Las estrategias de compactación fusionan datos históricos detallados en resúmenes más gruesos (diarios, semanales, mensuales). Ambas son importantes para sistemas de producción de larga duración con entidades de alta escritura.

Diferimos estas funcionalidades deliberadamente. El modelo temporal al noventa y cinco por ciento está listo para producción para las aplicaciones que FLIN tiene como objetivo. Las políticas de retención son una optimización para escala, no un prerrequisito para la funcionalidad. Cuando una aplicación FLIN comience a generar suficientes datos históricos para justificar la limpieza automática, las primitivas para implementar la retención ya están en su lugar (los métodos prune_versions_before y prune_versions_keep_last de la Sesión 012).

Lo que esto significa para los desarrolladores

El modelo temporal de FLIN cambia la economía del desarrollo de aplicaciones. Funcionalidades que antes requerían infraestructura dedicada -- registros de auditoría, funcionalidad de deshacer, informes de cumplimiento, historial de precios, registros de actividad, recuperación de datos -- ahora son gratuitas. No "gratuitas como en bajo costo". Gratuitas como en cero código adicional.

Un desarrollador construyendo una plataforma de comercio electrónico en FLIN obtiene historial de precios, registros de auditoría de pedidos y registros de actividad de usuarios sin escribir una sola línea de código de seguimiento. Un desarrollador construyendo un sistema de gestión documental obtiene historial de versiones, detección de cambios y reversión sin un framework de event sourcing. Un desarrollador construyendo una aplicación regulada obtiene registros de auditoría de grado de cumplimiento sin una base de datos de auditoría separada.

Esto no es una mejora marginal. Es un cambio de categoría. El modelo temporal elimina toda una clase de infraestructura de aplicaciones. Hace por el historial de datos lo que la recolección de basura hizo por la gestión de memoria: elimina la carga del desarrollador y la maneja automáticamente, correctamente y eficientemente.

"E flin nu" -- Recuerda las cosas

La frase "E flin nu" proviene del fon, un idioma hablado en Benín. Significa "recuerda las cosas". Se convirtió en el lema del modelo temporal de FLIN durante el desarrollo, un recordatorio de lo que estábamos construyendo: un lenguaje donde los datos recuerdan.

No datos que puedan configurarse para recordar. No datos que recuerdan si instalas el plugin correcto. No datos que recuerdan si configuras triggers y tablas de historial y middleware de auditoría.

Datos que recuerdan porque eso es lo que hacen los datos en FLIN. Siempre. Automáticamente. Desde el primer save hasta el último destroy.

Ciento cincuenta y dos tareas. Diez categorías. Seis días. Y un lenguaje de programación donde cada entidad recuerda todo.


Esta es la Parte 10 de la serie "Cómo construimos FLIN" sobre el modelo temporal. La serie completa documenta el diseño, la implementación, la depuración y la finalización del modelo de datos temporal de FLIN -- una funcionalidad que ningún otro lenguaje de programación ofrece como primitiva del lenguaje.

Navegación de la serie: - [046] Every Entity Remembers Everything: The Temporal Model - [047] Version History and Time Travel Queries - [048] Temporal Integration: From Bugs to 100% Test Coverage - [049] Destroy and Restore: Soft Deletes Done Right - [050] Temporal Filtering and Ordering - [051] Temporal Comparison Helpers - [052] Version Metadata Access - [053] Time Arithmetic: Adding Days, Comparing Dates - [054] Tracking Accuracy and Validation - [055] The Temporal Model Complete: What No Other Language Has (estás aquí)

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles