paldepind / flyd

The minimalistic but powerful, modular, functional reactive programming library in JavaScript.
MIT License
1.56k stars 84 forks source link

Laziness #24

Closed iofjuupasli closed 6 years ago

iofjuupasli commented 9 years ago

Implementing laziness would be quite easy. But it definitely has some negatives impacts on user friendliness.

Please make it without impact on possibility to get value of stream (by default, but it's ok to provide different methods with laziness enabled), and so it'll not create problems with stateful streams.

In theory it looks like not so big issue. But when I tried to build idiomatic FRP code with kefir, it was extremely irritatingly. The main problems was with getting current value, and stateful streams. Also there was problems with atomic updates. Bacon sample have broken when I tried to juggle with its restrictions. And it was issue of bacon because same code on kefir works good, although code looks horrible.

But when I tried flyd it was so simple to build whatever I want. I stopped worrying about laziness, many restrictions, and started to think more about logic. Most of streams was static, so garbaging it wasn't an issue. But in some places where I create streams in loops it's possible to use end (or use helper that ends temporary stream when needed as in discussion of this issue).

Also we should have choice. Flyd interestingly different from other FRP libs. I think many will appreciate advantages of flyd more than its disadvantages.

paldepind commented 9 years ago

Thank you for opening and issue on this. I think it is needed.

Please make it without impact on possibility to get value of stream (by default, but it's ok to provide different methods with laziness enabled), and so it'll not create problems with stateful streams.

If a stream is inactive its value will not be updated and thus getting it's latest value won't work. Now, streams could optionally be lazy but that might make usage even more confusing.

Most of streams was static, so garbaging it wasn't an issue.

I think that is a good argument. I'm used to functional reactive programming from Elm. And in Elm all streams (they call them signals) are static. If you do FRP in that style then laziness does not matter.

I am very happy to hear that you find the style that Flyd makes possible liberating. I am of the same opinion and would be reluctant to give that up.

But in some places where I create streams in loops it's possible to use end (or use helper that ends temporary stream when needed as in discussion of this issue).

That as well is a great point. As I mentioned in the discussion about switchLatest #8 Flyd could easily provide function that automatically would end streams in the common cases where one uses dynamic streams.

I will have to look a bit more into how the other JavaScript FRP handles this. It has certainly given them a fair share of problems. A few links of relevance: https://github.com/pozadi/kefir/issues/97 – my conversation with @pozadi about lazyness https://github.com/pozadi/kefir/issues/43 – A Kefir issue on the matter https://github.com/baconjs/bacon.js/issues/536 – A Bacon issue on the matter

jasonkuhrt commented 9 years ago

Can you point me to the literature around this topic of static graphs versus lazy dynamic ones? I've read Evan's thesis paper but haven't explored much else such as Conal's original and subsequent work. I'm guessing @conal would have useful feedback if he were so kind to share it ; )

paldepind commented 9 years ago

@jasonkuhrt This video explains quite a bit.

jasonkuhrt commented 9 years ago

Yeah seen it. Will review again though. Need to re-read and test and re-read and test a lot before all the ideas sink in.

conal commented 9 years ago

I don't know what your goals are for this system, e.g., whether you intend to correctly implement FRP in the original, well-specified sense or something vaguely FRP-inspired. To get some idea of what I mean by this distinction, see my answer to "Specification for a Functional Reactive Programming language" and follow links from there for more info. Once I understand your goals better (particularly what specification you're trying to implement correctly, if any), I might be able to help.

AlexGalays commented 8 years ago

Without laziness, how do you realistically handle resource management / memory leaks?

In general, it's hard to follow which streams need to be ended manually, and which ones are automatically ended by some combinator.

paldepind commented 8 years ago

Imagine I create a domEvent stream : function domEvent(element, eventName) Presumably, it adds the eventListener eagerly in the domEvent function since there is no concept of whether the stream has demand. When should it stop listening?

One approach would be to know then the element is being removed from the DOM. Then you can end the stream at that point.

stream(promise) doesn't end when the promise is resolved. This seems counter intuitive?

I'd say that is a bug.

In general, it's hard to follow which streams need to be ended manually, and which ones are automatically ended by some combinator.

If you are imperatively pushing to a "top-level" stream, then you have responsibility for ending it. But yes, I agree, it can be hard.