.NET event sourcing library with CosmosDB, DynamoDB, EventStoreDB, message-db, SqlStreamStore and integration test backends. Focused at stream level; see https://github.com/jet/propulsion for cross-stream projections/subscriptions/reactions
Changes the standard signatures to remove variation that was not earning its keep:
Changed the standard fold signature from 'state -> 'event seq -> 'state to 'state -> 'event[] -> 'state
Very few stores did not already internally have them as arrays; it's not a perf concern
Means let fold = Array.fold evolve never needs type annotations
More clearly conveys that states tend to be evolved in batches (and e.g. that if you are able to derive a state from the last event, you can do an Array.last)
In tests, using [| comprehensions vs seq { tends to produce less noise
Changed the Decider's interpret and decide function signatures to use [] instead of list
The current implementation wants and needs it to be an array in the end, in order to be able to determine whether a Sync is required, so any perf or allocations argument is not relevant
While there are some cases where cons'ing a list together provides a pleasing solution, the advent of array comprehensions with implicit yield in F# 6 is simply a better more general pattern
Changed DeciderCore's interpret and decide function signatures to use [] instead of seq
While C# provides the generator pattern for IEnumerable via yield break...
they should also implement array comprehensions; I'm betting it'll happen this side of 2030
the core impl materialises it as an Array anyway, so no perf gain
Conversion overload shims can be added pretty easily
The Decider impl can more directly use the Array impls, which makes the whole thing more legible
This leaves the key differences between the Decider and DeciderCore interfaces clearer:
Func vs FSharpFunc (mapped internally to Func)
passing CancellationTokens everywhere vs everything just working
structValueTuples vs System.Tuple (open to nudging on this)
the perf overhead of sprinkling in ValueTuple.Create is not the point, async is already way more allocation heavy anyway
switching between async and task atm involves introducing struct noise in code
Propulsion's F# interfaces atm don't use struct tuples either
The base of this work is identical to #409; Props to @nordfjord for thinking it could be done, nudging me for ages, and then proving it!
Changes the standard signatures to remove variation that was not earning its keep:
fold
signature from'state -> 'event seq -> 'state
to'state -> 'event[] -> 'state
let fold = Array.fold evolve
never needs type annotationsArray.last
)[|
comprehensions vsseq {
tends to produce less noiseDecider
'sinterpret
anddecide
function signatures to use[]
instead oflist
Sync
is required, so any perf or allocations argument is not relevantyield
in F# 6 is simply a better more general patternDeciderCore
'sinterpret
anddecide
function signatures to use[]
instead ofseq
IEnumerable
viayield break
...Decider
impl can more directly use the Array impls, which makes the whole thing more legibleThis leaves the key differences between the
Decider
andDeciderCore
interfaces clearer:Func
vsFSharpFunc
(mapped internally to Func)CancellationToken
s everywhere vs everything just workingstruct
ValueTuples
vsSystem.Tuple
(open to nudging on this)ValueTuple.Create
is not the point,async
is already way more allocation heavy anywayasync
andtask
atm involves introducingstruct
noise in codeThe base of this work is identical to #409; Props to @nordfjord for thinking it could be done, nudging me for ages, and then proving it!