StreetStrider / fluh

simple & easy functional reactive library with atomic push strategy
Other
17 stars 1 forks source link

Project status #1

Closed antonkatz closed 4 years ago

antonkatz commented 4 years ago

Hello! Thank you for making an awesome library.

I see that there are some recent updates, and it's more active that flyd. I'm thinking of trying this out for a experimental production project, would you recommend me against that?

StreetStrider commented 4 years ago

@antonkatz, Hello. You're welcome to try. It's an experimental package of mine, but it is heavily tested and I'm currently using it in a couple of private repos. It's pretty stable and I have had zero issues with it for a long time.

I'm interested in developing fluh, so, in case you're in, I'm here for discussion and help.

antonkatz commented 4 years ago

@StreetStrider Awesome :) I've been working with rxjs for a little while now, and I've run into issues using it the way I want to, which is simply using it as a dependency graph for event-driven computations. I've just taken a look at hareactive and it too seems to be overly complex for this one specific task. Thank you very much for offering your help. One question that I have is what happens when a Bud emits Many values. While the graph is stabilizing/computing the first value, what happens to the others? Are they queued? How big can the queue get? Is fluh compatible with iterators? As in is there a mechanism to ask for the next value only after all the computations have occurred? I guess that can be achieved with the on effect, right?

antonkatz commented 4 years ago

I guess with the first question about what happens with Many, I'm wondering if hooking up fluh to a websocket is a good idea or a bad idea.

StreetStrider commented 4 years ago

@antonkatz I'm hooking it to a websocket in that manner: (pseudocode):

websocket.on(event_name, bud.emit)

My real code is:

realtime.stream = (channel) =>
{
    return Bud()
    .thru(tap(bud => realtime.on(channel, bud.emit)))
}

In that case Many is not used, it is just a series of different events which triggers series of bud emits (very common situation for fluh).

Many is a special case of working with map in fluh, where, for example, you need to double events in stream. In most of the cases it is not needed. This special cases described here. I believe, you will never need to use it.

As in is there a mechanism to ask for the next value only after all the computations have occurred?

All emits (and event graph in general) is resolved synchronously, unless you explicitly use some high-order things, which introduces async. In most of the cases you can be sure event graph is in consistent state after every single emit.

In general, first all graph is settled, then, all effects is fired (as described here). You can freely get the tip of the bud (bud.value) in any dependent effect and all values will be consistent (if no explicit async transformations in play).

StreetStrider commented 4 years ago

My usual workflow, including websocket is:

realtime.stream('event_name') → bud.map & filter → bud.on(post to dom)

I use Booth for websocket, then fluh, then blade for precise DOM transformations.

It is for sure, Booth can be replaced with socket.io, and blade can be replaced with morphdom, virtual-dom, snabbdom or React.

antonkatz commented 4 years ago

You have a collection of very awesome libraries. I might give Booth a try too! Thank you for your hard work. I'll let you know how my adventure with Fluh goes. I love it's simplicity. I'm thinking of using it with https://github.com/suguru03/aigle, for all the database access operations I do.

StreetStrider commented 4 years ago

@antonkatz OK, I will stanby for updates from you. For now the only documentation for fluh is readme and changelog.

I believe, you may need some clarifications on fluh operators, feel free to file a ticket if such. Some brief info: map/ — operators for map, thru/ — operators for high-order transformations, and lib/ — some core operators and library itself. The operator list is very narrow (I add operators when I need them), so you can invent your own operators and we may also consider adding them to the lib. I've noticed that set of operators is already so, that they can be combinated to achieve more complex tasks, but it does not exclude that some operators are still desired (like scan or other reduce-like operator).

Good luck!

antonkatz commented 4 years ago

I guess my final question is how is the performance? Have you benchmarked it against any other libraries?

StreetStrider commented 4 years ago

@antonkatz I've benchmarked it against flyd. It was at 1/1 ratio with flyd until I had implemented graph caching, which gives excellent results on static graphs. Right now it's x2 times faster than flyd. Copypasta from one of the recent perfs:

  diamond (FLUH):
    1 087 860 ops/s, ±0.45%   | 4.34% slower

  diamond (flyd):
    585 745 ops/s, ±1.24%     | 48.49% slower

  deep linear (FLUH):
    41 608 ops/s, ±0.34%      | 96.34% slower

  deep linear (flyd):
    18 140 ops/s, ±2.08%      | slowest, 98.4% slower

This result is totally doable in flyd (I believe flyd still can beat fluh, because fluh is slightly more abstract, where flyd is a single JS file with pretty plain cycles), but we never spoke with Simon on that, since he hasn't been working on flyd for a year and a half.

Here's my comment on perf compared to non-event graph.

antonkatz commented 4 years ago

Awesome. I'm sold! No more questions. :1st_place_medal:

StreetStrider commented 4 years ago

@antonkatz, I've prepped release 0.5.0 which was a master for a time. This is exacly the same version I currently use. npm i fluh will deliver ES5 version (so it works both Node and browsers), however, you can npm i StreetStrider/fluh#0.5.0 to install ES6 version from GitHub. I personally prefer it for the Web. I use Rollup, so I benefit from better bundles; you can try GitHub version too, even with other ES6-bundler.