Why Flow Treats Time as a Language Feature

Published: August 31, 2026 · Read Time: 7 min read · Category: Scientific Computing

Author: Abhishek Shivakumar (Systems & Audio Engineering)

Flow's future-looking idea is not new syntax for old programs. It makes evolution, effects, differentiation and deployment constraints part of the same executable model.

Most Languages Describe Steps

Mainstream languages are good at describing computation as a sequence: receive an input, transform it, return an output. Systems that evolve through time are forced into that shape. The model becomes a loop, the derivatives become callbacks, the solver owns the clock, and units or sample rates survive as comments if they survive at all.

That translation is so familiar that it looks inevitable. A physical model may begin in a notebook, move into a simulation package, become controller code, then be rewritten again for a device. Every boundary creates another representation of the same system and another opportunity for the representations to drift.

Flow starts from a different primitive. A program can say that angle evolves as velocity and velocity evolves as a function of gravity and damping. The compiler can hand those relations to a solver and emit native code. The mathematical description is not documentation adjacent to the implementation. It is the implementation.

That is Flow's most future-looking capability: it treats evolution as something the language should understand rather than a convention every library must reconstruct.

The Model and the Program Are One Object

When the model is executable, several traditionally separate tools can operate on the same representation. Simulation can inspect it. Control analysis can reason about it. Automatic differentiation can derive sensitivities. A backend can lower it to portable C, MLIR, WebAssembly, Metal or a domain-specific runtime. Tests can compare those paths without translating the model by hand.

This is more than convenience. It preserves intent. A generic compiler sees arithmetic and memory operations after domain meaning has been erased. A language that knows a value is state, a parameter, a derivative or a timed signal can retain useful structure deeper into compilation.

The long-term opportunity is a compiler that can answer domain questions before deployment: are the dimensions consistent, is the requested sample rate compatible with the graph, is an integration scheme appropriate, does a controller meet its timing contract, and where should state live on the target? Flow's thesis is that these are type and compilation questions. They are not paperwork performed after code exists.

Design direction

Preserve the information engineers usually lose between model, simulation and deployment, then let one toolchain analyse and lower the same source.

Time Belongs Beside Type

Software types traditionally describe the shape of a value. Real systems also care about when a value is valid, how frequently it changes, which precision it requires and what physical quantity it represents. An audio buffer at 48 kHz is not interchangeable with one at 44.1 kHz merely because both are arrays of floats. A distance is not a duration because both happen to be f64.

Flow's language direction brings units, sample rates, timing contracts, memory topology and numeric precision toward the type system. This can make whole categories of integration error visible before the program reaches a device. It also gives the compiler information that ordinary optimisation passes have to guess.

The difficult part is avoiding a type system so ceremonial that engineers escape it. Flow's opportunity is to make constraints inferable and composable: precise where the boundary demands precision, fluid where ordinary code does not. If successful, the language can make the correct thing easier without making every prototype read like a proof.

Effects Separate Intent from Execution

Algebraic effects give Flow another kind of use. Code can express that it needs an operation such as logging or I/O without fixing the concrete handler at the call site. A test can provide a deterministic handler, a desktop build can use the operating system, and an embedded target can provide a constrained implementation.

That separation is especially useful for simulation. The same system description may need real hardware in production, recorded inputs in a regression test and synthetic signals during design. When effects are explicit, changing the environment does not require threading a new framework-specific interface through the model.

Effects also offer a structured answer to a problem that async functions, dependency injection and global mocks each solve only partially. They make capabilities visible in the program's semantics. That improves testability today and creates room for schedulers, sandboxes and deployment tools to reason about what code is allowed to do.

Differentiation Should Not Be a Separate World

Modern engineering increasingly needs gradients. Machine learning is the obvious example, but system identification, inverse problems, controller tuning, differentiable rendering and scientific optimisation all ask how outputs change with parameters.

Today that often means moving the model into a tensor framework and accepting its execution model. Flow instead includes forward-mode dual numbers and reverse helpers in its standard library, with compiler-integrated gradient syntax on the roadmap. The design goal is for differentiation to compose with ordinary statically typed programs and with the same dynamics used for simulation.

This does not mean the current compiler has solved arbitrary differentiation. It means Flow treats derivatives as a language-level concern worth integrating with effects, types and backends. That combination is forward-looking because it joins fields that are usually separated by tool boundaries.

Portable C Is a Strategic Backend

A research language can have beautiful semantics and still fail at the final metre. Hardware vendors, audio hosts, operating systems and embedded SDKs already speak C. Flow emits portable C by default, with optional MLIR, WebAssembly and Metal paths. That makes the language an addition to existing toolchains rather than a demand to replace them.

C output is also inspectable. Teams can review what crosses the boundary, compile it with familiar tools, profile it with platform instruments and integrate it into a larger C or C++ system. For a young language, this is a pragmatic way to separate frontend innovation from backend adoption.

The same logic applies to the foreign-function interface. A language for physical and real-time systems must meet existing numerical libraries, drivers and runtimes where they are. Interoperability is not a concession around the language; it is part of the language's route into practice.

The Ecosystem Is Part of the Proof

A language is not validated by a feature list. It is validated by the programs that strain the design. Flow's repository contains hundreds of examples across games, morphogenesis, neurodynamics, evolutionary systems, procedural generation, machine learning and real-time graphics. Companion projects explore a Doom port, a scikit-learn-shaped API, reinforcement learning, Project Euler, editor support and AI-assisted development.

These projects are not proof of production maturity or broad adoption. They are executable questions. Can the language carry a large inherited system? Can its numerical abstractions express familiar machine-learning workflows? Can ordinary short programs remain readable? Can an assistant work in a language absent from its training data when given a current skill pack and a compiler-based verification loop?

The project is unusually candid about incomplete surfaces. Its self-hosted compiler can compile itself to a byte-identical fixed point, while the Python-host compiler still carries the full language and the bootstrap parity suite records remaining failures. Compiler-integrated gradient syntax is on the roadmap. That distinction between demonstrated capability and intended capability is essential for taking the work seriously.

Conventional boundaryFlow's direction
Model in a notebook, implementation elsewhereThe model is the executable program
Solver loop encoded by a libraryEvolution expressed in the language
I/O fixed at call sitesEffects handled per environment
Gradients delegated to a tensor frameworkDifferentiation composes with the standard language
Deployment requires a manual rewritePortable C and specialised backends lower the same source

Future-Looking Does Not Mean Finished

Flow is young. Its ecosystem is small, some ambitious features remain under construction, and the language must still prove that its abstractions scale under independent use. Stability, diagnostics, debugging, package governance and predictable performance will matter at least as much as the elegance of its thesis.

But future-looking languages are valuable before they are finished because they change what can be asked of a compiler. Flow asks why time is hidden in loops, why effects are buried in frameworks, why derivatives live in a separate programming world and why deployment discards the information used to design a system.

Its future is not guaranteed by combining fashionable features. It depends on making those features cohere around one durable idea: systems evolve, and software should be able to preserve that fact from equation to executable. If Flow succeeds, the gain will not be fewer lines of code. It will be fewer lossy translations between the thing an engineer means and the thing a machine runs.

Explore the current language and its evidence at flooooooooooow.github.io/flow, github.com/flooooooooooow/flow, quilio.dev/flow and quilio.dev/flow/ecosystem.

Back to Quilio Blog