v0.40.0
Tidal Threads
Compared to v0.39.0, Neva v0.40.0 redesigns streams as an explicit tagged-union protocol, expands immutable list operations, and makes common scalar collections materially faster without changing their public Neva types.
Highlights
stream<T>is now the tagged unionOpen | Data T | Close, rather than a struct carrying an implicit index and terminal flag. Stream boundaries and payloads are explicit, and an index is created only when a program asks for one withstreams.Enumerate<T>.streams.Just<T>adapts one value to a stream, whilestreams.Enumerate<T>adds explicit zero-based indexes when they are needed.lists.Append<T>is the renamedlists.Push<T>operation. Together with newPrepend<T>andConcat<T>, it provides immutable list updates.- Scalar
list<T>anddict<T>values now use native typed runtime storage where possible. Scalar-list traversal is 75–87% faster; constructing a 512-int list is 82% faster, uses 64% less memory, and makes 255 fewer allocations. neva tool <name>provides a consistent CLI entry point for an installedneva-*developer tool, includingneva tool lsp.- Compiler-version mismatch errors now name the workspace module, point to its
neva.ymlorneva.yaml, and recommendneva upgrade.
Streams: a model with explicit states
Previously, every item in a stream<T> carried control metadata even when a program only needed the value:
pub type stream<T> struct {
data T
idx int
last bool
}
Now a stream is a tagged union with exactly three possible states:
pub type stream<T> union {
Open
Data T
Close
}
Open marks the beginning of a stream, Data carries a value, and Close marks completion. Sources such as streams.Range, streams.FromList, and streams.Just emit this protocol; ordinary user code normally passes streams to standard combinators rather than handling the three cases itself.
This is Neva's return to the canonical FBP stream design: bracket information packets delimit a sequence of ordinary data packets. It follows the model described by FBP pioneer J. Paul Morrison, with whom Neva's author corresponded while developing the language. The earlier indexed structure was an intermediate design; experience showed that the explicit FBP protocol is the more idiomatic and robust model.
streams.Map, Filter, ForEach, and other combinators use Switch<stream<T>> internally: they forward Open, transform or handle Data, and forward Close. The three paths are intentionally shown only as a schematic here — the actual Map graph also has a FanIn and ordering/backpressure wiring that must not be omitted:
Open -> ... -> Open
Data -> handler -> Data
Close -> ... -> Close
The model makes the protocol visible in the type system, removes a stored index from the default representation, and keeps indexing as an opt-in operation.
streams.Just<T>
pub def Just<T>(data T) (res stream<T>)
Just adapts one value to an API that expects a stream. Given 42, it emits Open, Data(42), then Close.
This matters when a higher-level API is stream-oriented but the caller has one value. For example, the PNG example creates one image.Pixel, turns it into a one-item stream with Just, and passes that stream to image.New:
new image.New
just streams.Just<image.Pixel>
---
newPixel -> just -> new
streams.Enumerate<T>
pub type Enumerated<T> struct {
idx int
item T
}
pub def Enumerate<T>(data stream<T>) (res stream<Enumerated<T>>)
Use Enumerate when an index is part of the program's logic. It attaches 0, 1, 2, … to Data items instead of making every stream item retain an index by default.
std/lists
This release adds three list operations. As with the rest of Neva's value operations, each returns a new list; the input list is not mutated.
lists.Append<T> — renamed from Push<T>
pub def Append<T>(lst list<T>, data T) (res list<T>)
The dataflow below appends 3 to [1, 2] and produces [1, 2, 3].
append lists.Append<int>
---
[1, 2] -> append:lst
3 -> append:data
append:res -> :res // produces [1, 2, 3]
lists.Prepend<T>
pub def Prepend<T>(lst list<T>, data T) (res list<T>)
Prepending 1 to [2, 3] produces [1, 2, 3].
lists.Concat<T>
pub def Concat<T>(left list<T>, right list<T>) (res list<T>)
Joining [1, 2] and [3, 4] produces [1, 2, 3, 4].
concat lists.Concat<int>
---
[1, 2] -> concat:left
[3, 4] -> concat:right
concat:res -> :res // produces [1, 2, 3, 4]
Faster scalar lists and dictionaries
This release includes a runtime optimization for common scalar collections. On the measured hot path, scalar-list traversal is 75–87% faster; constructing a 512-int list is 82% faster and uses 64% less memory. At the language level, list<T> and dict<T> are unchanged.
Internally, homogeneous scalar values no longer have to be stored as a generic runtime-message box per element when a typed representation is available.
Conceptually, the runtime can now keep an integer list or dictionary in native typed storage:
before: []Msg{Int(1), Int(2), Int(3)}
after: []int64{1, 2, 3}
before: map[string]Msg{"answer": Int(42)}
after: map[string]int64{"answer": 42}
Heterogeneous and non-scalar values retain the existing generic representation. The runtime boxes values only at boundaries that actually require generic messages.
On an Apple M1 Pro with Go 1.26.5, the benchmarked implementation delivers:
- scalar-list traversal: 75–87% faster for 8–1024 integer items;
- construction of a 512-int list: 82% faster, 64% fewer bytes, and 255 fewer allocations;
- construction of a 512-int dictionary: 10.6% faster, 33.7% fewer bytes, and 255 fewer allocations;
- full
list_atandlist_slicepaths: 3–4% faster with one to two fewer allocations; - dictionary lookup: neutral in the full port path; no regression observed.
The detailed benchmark method and raw comparison are in PR #1127.
Developer tools
Run an installed language server through the Neva CLI:
neva tool lspneva tool is a dispatcher for installed executables named neva-<name>; it does not install or update tools itself.
Smaller developer-experience improvements
- Compiler-version mismatch errors now identify the affected workspace module, show where its declared version lives, and suggest
neva upgrade. - Tagged-union documentation now matches the compiler: declare tagged
union { ... }types rather than the removed untagged-union syntax.
Included PRs
- #1042 Redesign stream as union and add
streams.Enumerate - #1047 Redesign stream item helpers and enumerate support
- #1127 Typed scalar list/dict containers and runtime fast paths
- #1144 Validate union literal payloads
- #1151, #1153, #1154, #1155, #1157 Runtime message and collection work
- #1158 CLI developer-tool manager
- #1161 Tagged-union documentation correction
- #1162 Compiler-version mismatch guidance
Documentation, CI, benchmark, and engineering-harness improvements in the same release window are included as maintenance work.