Live data from Hacker News

Managing State with Signals

tonsky.me

71–80 of 126 posts

Re: Managing State with Signals

#71

Earlier quoted context omitted.

Thanks for this thoughtful and insightful comment. Many problems can be workflow problems, sometimes even pulling in a rule engine, or require a job queue to do things that can fail. Then you have software such as https://temporal.io/ which is really powerful for resilient workflows. Imagine coordinating the user with a workflow with asynchronous data collection steps. Imagine programming "reaction to user behaviour…

This workflow engine of yours seems to expect a very predictable and linear sequence of actions. What happens if the user clicks "log out" after user.tutorial(); It's basically making an already easy case easier.

Even linear workflows can handle conditional execution, you just lift the condition into a value, like into a Maybe/Option type, and later stages only execute if there's a value.

Or if you want to be more literal, declare a specific type:

    type Authenticated(User) = No | Yes(User)
And each stage of your workflow matches on Authenticated.Yes, and so only executes if the user is authenticated. That's basically what any system does internally, this just makes that implicit behaviour explicit.

Re: Managing State with Signals

#73
Not sure I like the mixed push/pull approach. If you're already traversing the tree to mark nodes as possibly dirty, you might as well recompute the node's value and store it while you're there. Otherwise on pull/lazy update, you're traversing the tree all over again! Terrible for cache locality, particularly for large graphs.

You might be tempted to say that the lazy approach might avoid some recomputations, but if a node isn't actually going to be accessed then that node is effectively no longer live and should be disposed of/garbage collected, and so it will no longer be in the update path anyway!

The mixed push/pull approach has only once nice property: it avoids "glitches" when updating values that have complex dependencies. The pull-based evaluation implicitly encodes the correct dependency path, but a naive push-based approach can update some nodes multiple times in non-dependency order. Thus a node can take on multiple incorrect values while a reaction is ongoing, only eventually settling on the correct value once the reaction is complete.

In other push-based reactive approaches, you have to explicitly schedule the updates in dependency order to avoid such glitches, so perhaps this push/pull approach was picked to keep things simple.

Re: Managing State with Signals

#74

Earlier quoted context omitted.

Signals have become enough of a buzz word that they may catch on just for the hype, regardless of whether they are particularly powerful or ergonomic.

I feel like signals have been a buzzword for almost 10 years now.

May just be my memory failing me or the circles I ran in over the years, but I remember the related buzzwords always being observables and streams. I hadn't seen much interest/hype in the actual term "signal" until solidjs used it and push heavily on comparisons with react hooks.

Re: Managing State with Signals

#75

It's interesting how every build system, frontend framework, programming language implements its own promise pipeline/delayed execution/observables/event propagation. But the implementations are rarely extracted out for general purpose usage and rarely have a rich API. I've been thinking a lot about a general purpose "epoll" which be registered on objects that change. I want to be able to register a reaction to a seq…

> It's interesting how every build system, frontend framework, programming language implements its own promise pipeline/delayed execution/observables/event propagation

This is exactly the reason to use state machines/data flows IMO.

Every implementation is unique enough that simply following how data flows through the different states and transitions, and where the sinks and funnels are, will tell you everything you need to know about what your system is actually doing at any point in time.

The challenge there is, things like that are a shitload of instrumentation and requires a lot of forethought to not just jam everything into a framework that puts boundaries on what you can design and implement. So for 99% of applications, it's not worth the hassle and you're better off with just basic text documentation.

Re: Managing State with Signals

#76

Earlier quoted context omitted.

I feel like signals have been a buzzword for almost 10 years now.

May just be my memory failing me or the circles I ran in over the years, but I remember the related buzzwords always being observables and streams. I hadn't seen much interest/hype in the actual term "signal" until solidjs used it and push heavily on comparisons with react hooks.

"Signal" has been used in FRP circles for some time [1,2]. The original FRP stuff was events/signals and behaviours. But I agree that JS didn't use this terminology until more recently. S.js is maybe one of the earlier ones, but that was still over 8 years ago.

[1] https://scholarworks.rit.edu/cgi/viewcontent.cgi?article=651...

[2] https://github.com/14427/signal

[3] https://github.com/adamhaile/S/tree/e897ec1212a073bb1fe695e1...

Re: Managing State with Signals

#78

The observable syntax is confusing. Lisp uses *earmuffs* syntax for global variables, if you only use one muff for an observable variable, how would you express a global variable that's observable? Using **lopsided muffs*?

Unicode to the rescue: *:eyes_emoji:earmuffs*

Re: Managing State with Signals

#80

Not sure I like the mixed push/pull approach. If you're already traversing the tree to mark nodes as possibly dirty, you might as well recompute the node's value and store it while you're there. Otherwise on pull/lazy update, you're traversing the tree all over again! Terrible for cache locality, particularly for large graphs. You might be tempted to say that the lazy approach might avoid some recomputations, but if…

I implemented the eager recomputation model for the observable utilities in vscode [1] and it quickly fell on my feet because of these glitches.

In particular this is problematic if you have observable optional state that has inner observable/derived state and someone reactively reads the outer state and then it's inner if the outer one is defined.

Then you clear and dispose the outer state and at the same time set some other observable value that the inner derived depends on. With eager recomputation, it can now happen that the inner derived is recomputed, even though the inner state is disposed.

[1] https://github.com/microsoft/vscode/blob/fe9154e791eafb4f18d...

Post reply on HN