Open RadicalZephyr opened 7 months ago
I still believe it may be possible to make simple computation a much lower cost proposition using a similar design to the Iterator
and Future
traits, of composition of generic types that can get monomorphized and inlined into much more optimized generated code.
I think that this will probably end up looking something like the trait structures that I have played with in my own implementations, somehow fused with the current Sodium design to allow sharing nodes when that's desirable.
Ultimately tho, from what I've seen of implementing Tic Tac Toe in Sodium, sharing nodes is the rule not the exception. Long chains of combinators without sharing intermediate results are not very common.
In fact, in my current design described above, the only reason that those three separate streams exist is to allow separate error checking, but I've now unified those three separate error streams into one Error type. This means that I could actually implement that as one map to do all of the parsing and validation that return a Result<usize, Error>
, compact all the processing into one Node
and then split that Stream<Result<usize, Error>>
into (Stream<usize>, Stream<Error>)
.
It's probably possible to do this in most cases where it would be natural to have a sequence of combinators.
In my currently quite limited experience with Sodium, I've noticed that
Streams
with the same types tend to abound, though each one will actually have different semantics. For instance, in my TicTacToe implementation, the parsing and validation process for the input index to play at. I have severalStream<usize>
representing variously:a usize in the valid range, and on an open board space
In Rust, it would be highly idiomatic to wrap these different values in newtypes naming the guarantees provided at each stage. In the case of when these values are already being computed on, it's trivial to do so in the same
filter_map
where you compute the new value.However, another case of carrying the same value through, but with different semantic meaning is for when you have a
Stream<()>
, i.e. aStream
that passes no data, just a signal. In this case, changing the type from one ZST to another introduces a newNode
to the Sodium graph, degrading the complexity making this no-op change carry a potentially significant performance penalty.It would be ideal for adding a simple
map
from one ZST to another to be a very low-cost or essentially free operation.