diff --git a/dcaf/streams/cashflows.py b/dcaf/streams/cashflows.py index 0635808..fdc2547 100644 --- a/dcaf/streams/cashflows.py +++ b/dcaf/streams/cashflows.py @@ -3,7 +3,7 @@ """ Core cashflow abstractions for discounted cash-flow analysis. -Provides CashFlow (immutable data point), CashFlowStream (functional container), +Provides CashFlow (immutable data point), CashFlowStream (functional-style container), CashFlowGroup (grouped container), and structured cashflow classification fields. """ @@ -254,9 +254,16 @@ class CashFlowStream(BaseStream[CashFlow]): Preserves insertion order and supports sequence-style operations (iteration, indexing, slicing, ``len()``) alongside domain-specific helpers for building, - filtering, transforming, grouping, and discounting cashflows. All - mutating-style operations return a new ``CashFlowStream``; the original is - never modified. + filtering, transforming, grouping, and discounting cashflows. Mutating-style + methods such as :meth:`append`, :meth:`extend`, :meth:`filter`, :meth:`apply`, + :meth:`sort`, and :meth:`scale` return a new ``CashFlowStream`` without modifying + the source stream. The container itself is not immutable: callers can mutate its + public :attr:`entries` list directly. + + Attributes + ---------- + entries : list[CashFlow] + Public mutable list containing the stream's frozen ``CashFlow`` values. Examples -------- diff --git a/dcaf/streams/generation.py b/dcaf/streams/generation.py index 3c1880c..153a6ec 100644 --- a/dcaf/streams/generation.py +++ b/dcaf/streams/generation.py @@ -3,8 +3,8 @@ """ Generation stream module for physical energy quantities. -Provides Generation (single data point), GenerationStream (container), -and GenerationGroup (grouped container) for modeling MWh production. +Provides Generation (immutable data point), GenerationStream (functional-style +container), and GenerationGroup (grouped container) for modeling MWh production. """ import datetime as dt @@ -204,8 +204,15 @@ class GenerationStream(BaseStream[Generation]): Mirrors the ``CashFlowStream`` pattern for physical energy quantities. Supports iteration, indexing, slicing, and ``len()``, alongside domain-specific helpers for building, filtering, transforming, and - converting generation to revenue or cost cashflows. All mutating-style - operations return a new ``GenerationStream``; the original is never modified. + converting generation to revenue or cost cashflows. Mutating-style methods + return a new ``GenerationStream`` without modifying the source stream. The + container itself is not immutable: callers can mutate its public :attr:`entries` + list directly. + + Attributes + ---------- + entries : list[Generation] + Public mutable list containing the stream's frozen ``Generation`` values. Examples -------- diff --git a/docs/api/streams.md b/docs/api/streams.md index 54c9d78..3ff30bb 100644 --- a/docs/api/streams.md +++ b/docs/api/streams.md @@ -1,7 +1,9 @@ # Streams -Lower-level financial and generation primitives. `CashFlow`/`CashFlowStream` and -`Generation`/`GenerationStream` are the immutable building blocks the +Lower-level financial and generation primitives. `CashFlow` and `Generation` are +frozen entry values. `CashFlowStream` and `GenerationStream` hold those values in +public, mutable `entries` lists, while their stream-producing methods return new +containers instead of mutating the source. These are the building blocks the [project builder](project.md) produces under the hood. Reach for them directly when you need fine-grained control — custom data sources, bespoke incentive structures, or transformations the builder does not expose. diff --git a/docs/concepts/streams.md b/docs/concepts/streams.md index 48490dc..e7af582 100644 --- a/docs/concepts/streams.md +++ b/docs/concepts/streams.md @@ -9,7 +9,7 @@ transformation the builder doesn't expose. Streams are also the clearest way to *what the builder produces under the hood*. ``` -DCAF's primitives are two parallel families of immutable, dated values: +DCAF's stream primitives are two parallel families of dated entry values and containers: | Single value | Container | Grouped container | |--------------|-----------|-------------------| @@ -22,8 +22,8 @@ A `CashFlow` is a frozen record: an `amount`, a `date`, a `label`, an `is_cash` (cash vs. accrual items like depreciation), a `pro_forma_category` (how it appears in a statement), and a `tax_treatment` (`TAXABLE`, `DEDUCTIBLE`, or `NONE`). -A `CashFlowStream` is a functional container. Every operation returns a *new* stream, -so chains never mutate the original: +A `CashFlowStream` is a functional-style container. Stream-producing methods return a +*new* stream, so chains using those methods do not mutate the original: ```python from datetime import date @@ -68,11 +68,26 @@ gen.sum() # total MWh `GenerationGroup` — a dict-like container of sub-streams supporting `aggregate(...)`, `apply_to_groups(...)`, `filter_groups(...)`, and `ungroup()` to flatten back. -## Immutability and composition +## Entry and container mutation semantics -All data classes are frozen and all operations return new instances. This makes -streams safe to share, easy to reason about, and naturally composable — the same -properties the [`EnergyProject`](energy_project.md) builder relies on. +`CashFlow` and `Generation` are frozen values. Their attributes cannot be reassigned. + +`CashFlowStream` and `GenerationStream` are not immutable containers. Their public +`entries` attributes are mutable lists, so callers can modify a stream directly through +that attribute. Methods such as `append()`, `extend()`, `filter()`, `apply()`, `sort()`, +and `scale()` follow a non-mutating contract: they return new streams without changing +the source stream. + +```python +original = CashFlowStream([CashFlow(100.0, date(2026, 1, 1))]) +scaled = original.scale(2.0) + +print(original.entries[0].amount) # 100.0: scale() did not mutate original +print(scaled.entries[0].amount) # 200.0 + +original.entries.append(CashFlow(50.0, date(2026, 2, 1))) +print(len(original.entries)) # 2: entries itself is mutable +``` ```{seealso} - [Streams API reference](../api/streams.md) for full signatures.