Keep values AND failures in the audit
Requirement
A deploy audit parses eight key=value config lines,
three of which have unparseable values. The report needs
both halves: print each successfully parsed
key = value, then a count of the failures. The lines are
in the code; both versions must print the output shown under
Expected output.
Expected output
timeout = 30 port = 8080 workers = 4 ttl = 300 batch = 25 failures: 3
Side by side
RxDart
FxDart
Why they differ
The stream model carries errors on a separate, out-of-band channel —
and that channel is terminal: one FormatException ends
the whole subscription, taking the five good lines with it. To keep
values and failures, the RxDart side has to give every line
its own inner stream (Rx.fromCallable) and convert the
error into data before it can escape —
onErrorReturn(null) here, with null standing
in for "this one failed". That is the lightest spelling the model
allows for a throwing function (the heavier materialize
route reifies full notification objects), and it exists only to undo
a decision the model made for you: errors were never values to begin
with. (Both panels share the same throwing parse on
purpose — with a null-returning parser both models could keep
outcomes as plain data; the throw is the premise, and what each side
must do about it is the comparison.)
The pull side never puts failures on a channel in the first place.
The same throw lands one local try/catch
away from being an ordinary value again — a nullable record — so the
whole requirement is map then partition:
one pass, two lists, both halves equally first-class. This is the shape of FxDart's broader typed-errors
stance (its Either pipelines are this same idea with
richer error types). When failures are part of the report rather
than an exceptional end, keeping them as data wins — the verdict
goes to FxDart.
Benchmark
N = 100
Time Tie
Peak memory Tie
N = 1,000,000
Time FxDart wins
Peak memory Tie
Bars are medians of repeated timed iterations in fresh processes per side (small N is batched for timer resolution). Sides within 5% of each other — or within 0.6 ms, a difference no person can perceive — count as a tie; close relative races are re-measured up to 5 runs. In an app, anything under a few milliseconds is invisible to the user regardless of which bar is shorter. Memory is peak process RSS. The Dart VM and the dataset are identical on both sides, so the difference between the two bars is what the pipeline itself holds onto.