Open kevinbaldor opened 3 years ago
Looks like a bug, I'll have a better look later when I get the time.
in a transaction the update-wise dependencies need to form a DAG (Directed Acyclic Graph).
doing something like let s2 = s1.snapshot(c1, ...)
. c1
is not considered an update depedency of s2
, the only update depedency of s2
is s1
in that example. Since cell's values are updated after end of transaction for view after current transaction they can be snapshoted without being considered an update dependency.
having a look at your sample code it seems that: (let a -> b mean a has an update depedency of b)
output -> output_cell_loop output_cell_loop -> new_output new_output -> output
So the update dependencies does not seem to form a DAG, I think that is where is it falling over (but not 100% sure). We may just need to do your self-modifying switch a different way.
This is not really exciting, but does the trick:
fn main() {
let sodium_ctx = SodiumCtx::new();
let sodium_ctx = &sodium_ctx;
{
let ss_input : StreamSink<i64> = sodium_ctx.new_stream_sink();
let sl_primes: StreamLoop<Vec<i64>> = sodium_ctx.new_stream_loop();
let s_primes = sl_primes.stream();
let c_primes = s_primes.hold(Vec::new());
let s_output =
ss_input
.stream()
.snapshot(
&c_primes,
|x: &i64, primes: &Vec<i64>| {
if primes.iter().any(|prime:&i64|(*x%prime)==0) {
None
} else {
Some(*x)
}
}
)
.filter_option();
sl_primes.loop_(&s_output.snapshot(&c_primes, |prime:&i64,primes:&Vec<i64>| {
let mut new_primes = primes.clone();
new_primes.push(*prime);
new_primes
}));
let l = s_output.listen(|x:&i64| println!("output {}",x));
for x in 2..20 {
println!("Sending {}",x);
ss_input.send(x);
println!();
}
l.unlisten();
}
}
Also when switching... for example let s1 = Cell::switch_s(cs)
, and say cs
is changing from an inner stream of s2
to s3
during the same transaction that both s2
and s3
fire. Then s2
will flow through for that transaction and s1
will behave like s3
after that transaction. And I think that is the real reason your seeing all the number instead of just in the prime numbers in the code you've shown.
This switch_s
test demonstrates the behaviour of switch.
I also posted this on the Sodium forum here: http://sodium.nz/t/a-problem-with-a-self-referential-switch/518
It turns out that the Java implementation also has an issue with my original (CellLoop) approach, but it does work when implemented as a StreamLoop.
Unfortunately, the StreamLoop version doesn't work in Rust.
To address the most recent comment from clinuxrulz, I agree about the semantics of switch. In fact, my code is relying on it. Each output of the switched stream is a prime and I want it to flow through on that transaction (because I want all primes to make it out), but then switched stream changes to reflect the addition of a filter that will block all future multiples of that prime.
I've posted another workaround here: http://sodium.nz/t/a-problem-with-a-self-referential-switch/518/6?u=clinuxrulz
I've resolved the bug in version 2.1.1, and Operational::defer
must be used to resolve node cycle.
See test:
https://github.com/SodiumFRP/sodium-rust/blob/dad65115ba3b627b7e9250188789ecfe12905839/src/tests.rs#L1504
I have attempted to write a sieve of Eratosthenes as a test of self-modifying FRP. The idea is that when fed with a stream of the integers from 2 up, it will emit the series of prime numbers. It works as follows:
Initially, the output stream is defined as equal to the input input -> output Whenever any value arrives at the output, a new stage is defined that takes that value and filters any even multiple of it. So as values are passed in it grows like 2: input -> output: yields 2, adds sieve(2) stage 3: input -> sieve(2) -> output: yields 3, adds sieve(3) stage 4: input -> sieve(2) -> sieve(3) -> output: yields nothing (the value is filtered by sieve(2)) 5: input -> sieve(2) -> sieve(3) -> output: yields 5, adds sieve(5) stage
and so on.
Unfortunately, this isn't working for me. I've attempted to strip it down to the minimal example that demonstrates the issue at https://github.com/kevinbaldor/sodium-rust-test/blob/main/src/main.rs.
I've inserted listeners with println! statements to demonstrate that subscribing to the output works as expected when taking snapshots of normal cells (demonstrated with a constant), but the snapshot of the CellLoop<Stream> that contains the output stream doesn't seem to work, the println! statements don't appear and the Cell::switch_s never changes from its initial value (the input).
It feels like this is something that should be possible. Is this a bug in sodium-rust, or am I using it incorrectly?