Pauan / rust-signals

Zero-cost functional reactive Signals for Rust
MIT License
670 stars 37 forks source link

Graph cycles and modifications #69

Open andrewbaxter opened 1 year ago

andrewbaxter commented 1 year ago

I'm assuming this is basically defining a callback graph (the documentation doesn't clarify this).

How does this handle graph cycles? Or graph modifications during event handling?

As someone checking out the library it would be great to have this documented up front.

Pauan commented 1 year ago

I'm assuming this is basically defining a callback graph (the documentation doesn't clarify this).

No, not at all, there are no callbacks. It uses the same Context / Waker notification system that Futures and Streams use. It's designed to work on top of the Future/Stream infrastructure.

How does this handle graph cycles?

There are no graph cycles. Rust's ownership model makes it (almost) impossible to create cycles.

I guess you could technically create cycles by using Broadcaster + switch, in which case it would probably just deadlock / livelock the Executor, just the same as any infinite Future / Stream.

Or graph modifications during event handling?

That is handled with the flatten or switch methods, which allow you to dynamically switch from one Signal to a different Signal. This works completely fine, and the implementation is very simple and fast. When the Signal changes it just drops the old Signal and starts polling values from the new Signal.

Note that Signals are intentionally not designed to handle events. Signals are lossy, they're designed to handle values that change over time. If you need events then you should be using Streams instead.

It's quite common for part of your program to use Streams (to handle events) and part of your program to use Signals (to handle values). Signals do not replace Futures/Streams, it complements them.

As someone checking out the library it would be great to have this documented up front.

That sort of thing shouldn't be in the tutorial, but I think it would be a good idea to create a separate document that explains the internal implementation details.

It sounds like you're interested in learning the internal details of the library, in which case you should first gain a deep understanding of Future/Stream, because the Signal system is almost identical to the Future/Stream system.

You can also ask any questions in our Discord server, I'd be happy to explain more about how the Future/Stream/Signal infrastructure works.

andrewbaxter commented 1 year ago

A document explaining the implementation details would be great.

And thanks, I thought this was higher level like the rx libraries. One thing they do is handle handler cycles by terminating execution when the same handler is called twice in a single event, but I doubt they're as lightweight as this.

Is this library primarily a user experience thing over streams/channels then, like the signals in sycamore?

Pauan commented 1 year ago

And thanks, I thought this was higher level like the rx libraries.

This is a very high level FRP library. It even supports dynamic Signals, which is something many FRP libraries don't support. And the seamless integration with Streams and Futures is also quite good, unlike many other FRP libraries. And our support for incremental collections (like SignalVec) is best-in-class.

One thing they do is handle handler cycles by terminating execution when the same handler is called twice in a single event, but I doubt they're as lightweight as this.

I don't see how that makes Rx "higher level". You shouldn't be writing cyclic Signals in the first place, so whether an FRP library terminates cyclic execution or not is rather irrelevant. It's like deciding whether a language is good or not based solely on whether it supports tail-call elimination.

Especially in a language like Rust, because Rust's ownership model makes cyclic Signals very difficult to create in the first place, and rightfully so.

And I would be very careful about comparing FRP libraries to Rx. Rx has a lot of issues and bad design, it's not a good FRP library (though it's a pretty decent stream library). So just because Rx does something a certain way, that doesn't mean it's correct or good.

There are a lot of trade-offs that are made when designing an FRP library, and because FRP libraries are so rare, many people make mistakes when creating FRP libraries. I myself made many mistakes designing FRP libraries before I created futures-signals.

Is this library primarily a user experience thing over streams/channels then, like the signals in sycamore?

No, not at all, Streams and Signals are fundamentally different both at the mathematical level and at the implementation level.

One of the major problems with libraries like Rx is that they try to unify Streams and Signals, which causes a lot of problems. FRP libraries that behave like Streams are fundamentally wrong in a deep way.

Like I said in my previous comment, Signals do not replace Streams, Signals complement Streams. Signals and Streams behave differently and are used in different situations.

You cannot replace Streams with Signals, and you cannot replace Signals with Streams, they are different concepts, and both need to exist.