Live data from Hacker News

Functional Reactive Programming

wiki.haskell.org

111–120 of 121 posts

Re: Functional Reactive Programming

#111
post #23

As someone who had to maintain two applications written entirely using the FRP Paradigm (Rx in Kotlin/Swift with a heavy focus on FRP principles), I am fascinated the idea but I absolutely hated the experience. Writing behaviour flows can end in beautiful blocks of easy to understand operations. However, as these get more complex and you need to combine multiple data streams, logic is scattered all over a module. Ref…

Do you have any thoughts on what would make a better experience?

From my experience working on the projects mentioned above as well as lots of Elixir code:

Things you can do:

* Use static typing. If not possible, use type annotations or proper pattern matching with a consistent structure (e.g. {:ok, val} | {:error, :reason} in Elixir).

* Keep abstraction at a reasonable level, do not abstract things simply because they might become useful.

* Wrap all values that flow in streams in custom data structures that can easily be expanded. Never (unless you're absolutely sure it never changes) use primitive types.

* Use FSM or any kind of modelled sequence to document what a module does (we ended up having mermaid diagram syntax as comments in some of the most complex modules)

* Make sure the language/library you use has good support for testing and especially debugging. Lots of FRP libraries don't or their implementation works against the design of the language which leads to all kinds of odd behaviours.

Things to be aware of:

* FRP enthusiasts often tout that you can use the same flow of logic across languages that provide FRP libraries. Yes, you kind of can but all the functions are named different, have different interfaces and it's been a huge mess for us.

* If you interface with libraries that rely on strict threading (e.g. C libraries using pthread), strap in for some pain. FRP libraries often do what apple does with GCD in where they kind of abstract threading away from you into queues, which works fine if you stay in the FRP world but falls apart when interfacing with thread sensitive code.

Re: Functional Reactive Programming

#112
post #69

Earlier quoted context omitted.

It was pretty obvious that at some point, someone would fork Elm, given how Elm has always preferred purity over practicality... but it's not clear how this fork improves things even after reading the (tiny) documentation... can you expand on that?

It seems to address the important problems with Elm, around its overly restrictive project management, by 1. introducing more flexible package management (e.g. you can depend on a fork of a core package, or a private git repo etc.) 2. being open to adding various missing web APIs (e.g. while I'm not sure it's there yet, I expect websockets to make a return, which were dropped with Elm 0.19) 3. generally going for an…

Lead developer of Gren here. It's great to see people being interested in the project, thanks for the mention!

Just wanted to add that both websockets and indexed-db is being worked on by members of the community. I'm hopeful we'll have them ready for the 0.2.0 release in December, alongside preliminary nodejs support.

Since the 0.1.0 release, we've added support for local- and session-storage, which was another often-requested feature for Elm.

Re: Functional Reactive Programming

#113
post #107

Earlier quoted context omitted.

It seems to address the important problems with Elm, around its overly restrictive project management, by 1. introducing more flexible package management (e.g. you can depend on a fork of a core package, or a private git repo etc.) 2. being open to adding various missing web APIs (e.g. while I'm not sure it's there yet, I expect websockets to make a return, which were dropped with Elm 0.19) 3. generally going for an…

> I expect websockets to make a return, which were dropped with Elm 0.19... Websockets were dropped?? It seems support was introduced with subscriptions in Elm 0.17[1] as a great feature?? How did that happen? [1] https://elm-lang.org/news/farewell-to-frp

The official reason is here: https://github.com/elm-lang/websocket

I believe it's quite possible to support websockets with the current language features, though, and I hope that websockets will be ready for Gren along with the 0.2.0 release in december.

Re: Functional Reactive Programming

#114

Angular is using rxJs which is a reactive programming framework, but I think it was an error to do so. The angular project I am working on is now 5 years old and the parts of the application that are the least understood are the ones with more rxJs in it. We even have custom rxJs operators that nobody understand anymore... The way we do things now is to transform everything we can into promises because it's more easy…

Yes and no. We found that rx.js is bad for coordination. I think coordination is a big part of services in angular UI's. You need to fetch some data, wait for it, ask for different thing, maybe change some state. Promises and await are great for this and especially await syntax is readable (go channels could be even better). In rx.js you had to nest multiple switchMaps for dependant queries and for state - you either…

> My personal issue with rx.js is that BehaviourSubject is leaky

RxJS best practice is that BehaviorSubject should be used infrequently to never, mostly as "internal plumbing" to things like building your own mini-Operators, and that they should never escape an API boundary. If you need to pass a BehaviorSubject to a consumer you always .asObservable() it and the consumer must treat it like a regular Observable (your API boundary is always Observable, never Subject). (BehaviorSubjects are an imperative "back door" that breaks building things the reactive way.) That's one of my biggest personal problems with the Angular core libraries is how many BehaviorSubjects leak out everywhere in that API design. EventEmitter is a big giant BehaviorSubject. The Routing APIs leak BehaviorSubjects. Angular's "Reactive" Forms leak imperative BehaviorSubjects all over the place. The bad use of BehaviorSubjects by the core libraries leaks to the rest of the Anuglar ecosystem and there are so many "Angular best practices" that are "RxJS worst practices" purely from these early API design decisions.

> We found that rx.js is bad for coordination.

RxJS is great at "coordination". It requires a different mindset. Angular is awful at helping you get into that mindset. Angular's HttpClient is especially "bad, heavy promises masquerading as Observables" which makes it hard to think of queries/API fetches as events that can return refreshed data over time (streams of fetch results), more similar to those user interactions you saw good results from. There's a lot of useful coordination operators in RxJS beyond `switchMap()` like `mergeAll()` and `concatAll()`. If you think of a stream of request events flowing into a stream of response events flowing into a state machine (possibly with a very simple, similar `scan` to your user interaction model to reduce your state over time, a little like the "redux" pattern [1]), RxJS can be brilliant for "coordination" as data arrives.

(Angular kind of sets it up to fail. With how HttpClient works. With how Async Pipe works and isn't the default.)

> or you put some `tap` or `subscribe` with `takeUntil`

This is also where Angular "best practices" and I diverge, and I think also stems from RxJS "worst practices". RxJS best practice is also to use `tap` as infrequently as possible. It's a worst chance escape hatch at best. RxJS best practice is also the `subscribe` as "late" as possible and also as infrequently as possible and that you never have a `subscribe` without an `unsubscribe` to clean up resources, including memory. (Which can be very important if you are trying to do everything the reactive way. You can move all your setup into Observables, including the setup and teardown of vanilla JS components.) The overuse of `subscribe` in Angular components seems one of the biggest obvious reasons why so much of the usage of Observables in the Angular ecosystem looks like bloated Promises. (Which is directly a bad example set by the core library's own HttpClient.) (I also think some of Angular "best practices" uses of `takeUntil` aren't great either. I was taught that Observable completion should "mean something" as it's a key event in the stream, and shutting down Observables early mean you miss later events.)

If Angular's template language took Observables directly, without needing an "async pipe", most of those manual subscribes would just vanish in an instant. Most of the needs for `ngOnInit` and `ngOnDestroy` "lifecycle events" disappear. (As Observables have lifecycle events already in subscribe/unsubscribe setup/teardown.) Angular could have not needed Zone.js at all nor its complex "Change Detector" apparatus. Angular could have done smart things in coordinating Observable observations by templates. (A lot of what React's last several major releases have been about in building its Concurrency, Suspense, and related systems out have been about among other things throttling "non-important" DOM updates together to things like requestAnimationFrame and doing very complicated work under the hood to set all that up from VDOM changes. In an Observable world you can pipe a `mergeAll()` through `debounce(0, requestAnimationFrameScheduler)` and get things like that "really easy".)

I wound up trying to encode all of my personal best practices for writing powerful, reactive components in Angular into an opinionated reusable "component framework": https://www.npmjs.com/package/angular-pharkas

I haven't yet found a good way to encode my mindset to "reactive service classes" in Angular, in part because "it feels obvious" to me and isn't really a pattern so much as a mindset, which I know it is exactly not obvious or lots of people would be doing it and Angular's ecosystem would be less full of bad examples. Probably a key place to start based on the above conversation is that I almost always wrap any calls to Angular's HttpClient in a "forever Observable". Whatever the input is to call that HTTP API, whether it is a refresh signal or some sort of other input event (Observable) hide the `switchMap()` in an Observable of results over time. Most "dependent" data streams are coordinated sometimes as simply as a `combineLatest()` and others are `scan()` reductions (even full "state machines" in some cases of those reducers). Everything is returned as Observable and never any Subject. Everything flows into the Components and `subscribes` are as late as possible (and these days hidden entirely away in "Pharkas" binds). Learn when to `share()` observables (or more often `shareReplay(x)`) and reuse existing Observables rather than build new ones.

I don't know how much that helps. I've been able to wring a lot of good reactive programming out of a massive Angular frontend, but I've been fighting Angular itself (and the terrible debugging/performance experience of the gross, unnecessary Zone.js), and the greater ecosystem of "Angular best practices" the whole way. Every Junior Developer with Angular experience that looks at the code for the first time generally thinks it is readable but that "it looks nothing like Angular I am used to". It's definitely not "Angular best practices" as people are learning them today.

Also, a useful related tip for removing most uses of `tap`: install `rxjs-spy`. It's great. It provides a very simple `tag('some-observable-name`)` operator that is a no-op in Production builds and in Development builds gives you a `window.spy` toolkit that lets you log events from tagged Observables (or a regex matching tagged Observable names) or even set debugger breakpoints at tags.

[1] Though I tend towards "lots of little observable streams" over combined ball of single state refiltered back into little observables like in the "proper" "redux" pattern or how tools like NgRx try to implement that in the Angular world.

Re: Functional Reactive Programming

#115
post #65

I'll be adding this to Inflex ( https://inflex.io/ ), I have the design worked out on paper. (But I'll be open sourcing it first and releasing as a desktop app, and then get back to dev.) It's also helpful to think of FRP in terms of "push" and "pull" (for which there's a related paper by the same chap). This refers to control flow. Behaviours are "pull" i.e. your program has to pull from them. Events are "push" i.e.…

I got the impression that you're already using FRP for inflex.

The UI is implemented using Halogen, a front end library for PureScript. That handles redisplay when recalculation happens. That’s more like React or Elm.

Rather, I mean exposing an FRP API to people using the Inflex language.

Re: Functional Reactive Programming

#116
post #67

Is it viable to build a traditional web product/company on top of Haskell? That sounds like a silly question - but I’m serious. I’m enamored by the beauty of FP, but I’m not sure if there’s enough tooling or libraries to get to market. For example - GraphQL. There are two packages (mu and morpheus), but neither clearly document their feature parity in relation to other packages for other languages - and things like d…

I have never tried, but my impression is basically "You could, but why would you?" Haskell had a big jump in popularity around a decade ago, but it didn't gain traction with the "i'll grind 17 hours a day to become a rockstar ninja whatever" demographic that every language seems to bootstrap itself off of. I won't speculate as to why, but the end result is that Haskell is missing a lot of "adapter"-style libraries an…

You would be surprised how much Haskell is practical for boring stuff, try it out http://learnyouahaskell.com/

Re: Functional Reactive Programming

#117
post #105

I'll be adding this to Inflex ( https://inflex.io/ ), I have the design worked out on paper. (But I'll be open sourcing it first and releasing as a desktop app, and then get back to dev.) It's also helpful to think of FRP in terms of "push" and "pull" (for which there's a related paper by the same chap). This refers to control flow. Behaviours are "pull" i.e. your program has to pull from them. Events are "push" i.e.…

Inflex looks really great! I love the no-grid + reactivity premise!

Cheers! Work stalled since having a baby a year ago, but I’m sure I’ll be able to return to it eventually.

Re: Functional Reactive Programming

#118
post #105

Earlier quoted context omitted.

Inflex looks really great! I love the no-grid + reactivity premise!

Cheers! Work stalled since having a baby a year ago, but I’m sure I’ll be able to return to it eventually.

I just had a baby myself and I don't see any time for side projects in my future any time soon...

Re: Functional Reactive Programming

#119

Earlier quoted context omitted.

Hum, I'll complain about calling it "being picky". We are up to 3 completely different theories about what the GP meant, and no way to tell them apart.

"Discerning"? :)

Anyway, I think you are correct, and I don't know any good name for software that reacts to the wall time.

Re: Functional Reactive Programming

#120
post #106

Earlier quoted context omitted.

> it does have the downside of being the Swift 5.5 runtime, which has several unfixed issues, unfortunately. any links for this? im curious as im using async/await on xcode 13.4 currently.

Can't find them at the moment (check the Swift forums), but the two big issues I've seen reported are both in TaskGroup. One is a runtime crash under certain conditions, the other a major performance bottleneck when adding thousands of child tasks to a group. Both have workarounds, but they can hit you when using back deployment or any OS that has the 5.5 runtime (iOS 15 - 15.3 for instance). Wouldn't be showstoppers…

got it, thanks, i'll check the forums again!
Post reply on HN