In a recent discussion on PiLuD, a question was asked regarding implications of stream processing relative to the stack-machine concept used by many languages today. I replicate my answer here:
Stream processing has low memory locality (touches a lot of code per element), and uses a lot of memory (many buffers). OTOH, it composes well (pipelines, merges), parallelizes well and in an understandable manner (early parts of a pipeline can run in parallel with later parts, and multiple pipelines can easily run parallel side-by-side), and has highly predictable performance characteristics (CPU, latency, memory costs).
Consequently, stream processing is a very effective basis for:
- high-performance, scalable, parallel or distributed computing
- real-time computing
- embedded systems where predictable memory is important
Stream processing is used heavily for these purposes, both in languages (such as Lucid and Lustre – synchronous reactive programming) and architectures (OpenGL shader pipelines; flow-based programming; etc.).
A benefit of stream processing is that it’s `natural` to associate state with locations in a stream. Even in a semantically stateless stream-processing system, developers can easily annotate stateful optimization: caching, memoization, diff processing and compression, filtering equal updates, stability, choking, and so on. By comparison, there is no `natural` way to associate state with a behavior on a stack. Stack activities are naturally ephemeral. Developers can work around the issue by adding a `heap` or static state to their model, albeit at cost of complicating their computation model.
There is considerable variation among stream processing systems regarding what elements of the stream `mean`, and how side-effects and state are supported. Elements in a stream might represent events or a time-varying value. Effects might be triggered on events, continuous, or shifted entirely to system edges. State might be internal, i.e. accumulate history while computing the stream, or externalized to a database or tuple space. There are many design concerns to weigh here, such as events and internal state allow a lot of expressiveness, but using only external state can support the language designer in ensuring useful properties like resilience against disruption or consistent view among observers. (Note: one can get the benefits of both external and internal state by using linear types to exclusively bind external state to an internal location.)
Consider: Functional Reactive Programming (FRP) and Reactive Demand Programming (RDP) are both stream processing models. FRP traditionally supports both events and behaviors, allows internal state (causal behaviors, event accumulators) but forces developers to propagate values to the edge of the system to integrate effects. RDP restricts developers to time-varying values, and forbids internal state, but does allow a continuous effects in the middle (including access to external state).
Anyhow, there are many stream processing models, just as there are many stack-based programming models.
IMO, stack-based programming survives on path dependence. It was a moderately effective design for a singular central processing unit and batch-processing applications. But it’s a rather poor fit for modern computation and service architectures – whether you’re considering GPGPUs, OpenCL, or something larger like web-services. And it’s a terrible fit for interactive or reactive applications, such as GUIs, games, and robot control software. While I mention complications for representing long-lived state, there are many other complications in the stack architecture – e.g. composing concurrent behaviors. Even if you do pursue stack-basis, you’d be better off at least modeling it as continuation passing rather than a true stack!
A stack is a fine example of a `simplistic` model that causes complications everywhere else! Stack-based programming hurts its users again, and again, and again. But it’s hard for humans to recognize such abuse until they’ve escaped it.
There comes a time when every language designer must make a choice between what is simple and what seems easy…