News for hackers. Stuff that matters.
Re:Functional Reactive Programming by cybrox on Tuesday August 16, 2022 @08:59AM (#32482174)
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.

Refactoring data-flows that go through multiple modules is a huge hassle. Sometimes, we would spend hours just refactoring data-passing, wrapping and unwrapping and tests surrounding modules, because we needed to pass some additional values.

It doesn't help that you have to set up all behaviours at setup time, which means the code is mixed with one-time setup code which regularly confused people working on the projects as to what is run at startup time and what is run per-event.

Debugging itself was mostly hampered by the libraries that we used not providing adequate tools for the job but even if they did, it was a lot more difficult to reason about compared to something like async/await based code or callback chains.

I can imagine FRP works better in purely functional languages but implementing FRP paradigms in general purpose languages - especially when interfacing with non-functional code, which is often necessary - has led to nothing but trouble for me.

Re:Functional Reactive Programming by amarant on Tuesday August 16, 2022 @09:21AM (#32482370)
Huh, my experience with Rx-Java is (partially) the opposite to yours. Partially because I've seen it used poorly in one project, and it was quite horrible, but that time it was more due to misusage of RX than it was the fault of RX itself.

The second time I've used it in a big commercial project it was used well, and that remains the best codebase I've ever worked with. Super easy to reason about, extremely performant, easily the cleanest asynchronous codebase I've ever seen.

I do believe surrounding tooling is important though. Part of why that codebase was so clean was because it used a framework that integrates very well with RX-Java (micronaut).

That said, I've never worked with await/async in any larger project, so I can't really compare the two fairly.

Re:Functional Reactive Programming by twawaaay on Tuesday August 16, 2022 @11:41AM (#32484301)
I am big fan of FRP with Java and RxJava/Reactor. When I learned about it three years ago I practically got drunk on it.

I think the issue is, the more power you get the more you have to be vigilant and more effort into ensuring the power is used wisely. The first project I joined that used RxJava learned this lesson very painfully.

In my projects I am ensuring some standards on how APIs are constructed and expected to behave so that composing large reactive systems is not getting out of hand very quickly.

Another problem is the steep learning curve for people who learned to mostly copy/paste code from Stack*. You can create very quickly what would normally be a very complex application with just few lines of code of Reactor, but it is not for free -- you still have to understand what it all does and how it all works or you will face consequences at some point.

Re:Functional Reactive Programming by cybrox on Tuesday August 16, 2022 @09:49AM (#32482691)
I agree that the surrounding framework (and I'd like to add also additional libraries) have a huge impact on this experience.

The codebase I have worked on might be a bit of an edge-case too, as it was used in realtime audio/video communication, so ms-timing, order of async operations and keeping a consistent state were absolutely necessary.

Using Rx for such complex, long-lived business logic is probably a long shot from using it for a cleanly structured SPA for example. I've used a lot of reactive concepts in applications that simply fetched and displayed data and in those cases, I really enjoyed it.

Re:Functional Reactive Programming by marcosdumay on Tuesday August 16, 2022 @11:39AM (#32484271)
FRP is like OOP. For some problems, there is a level of granularity where it's an absolute killer, and nothing gets even nearly as effective. But if you write it with too much granularity, it will completely destroy your code. (And with too little granularity, it will be useless.)

This phrase alone: "data-flows that go through multiple modules" is a very good indication that you broke things down too much, and should have ditched the FRP abstraction on a higher level.

Re:Functional Reactive Programming by cybrox on Wednesday August 17, 2022 @05:37AM (#32494132)
I absolutely agree. We took over the codebase from another team and they really took clean code abstraction to the next next level while using every last detail the FRP libraries provided.

It's better when you use a reasonable level of abstraction and think hard about maintainability of FRP code beforehand.

Re:Functional Reactive Programming by josteink on Tuesday August 16, 2022 @09:35AM (#32482500)
> 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.

I had a similar experience maintaining and reviewing an Angular+Typescript application at work. I was fascinated about RX and was looking forward to see it in action.

In practice Angular exposes everything from its internal framework and interfaces as Observables, making RX everywhere the default.

The simplest things became mindbogglingly complex and asking “what does this code actually do” became the most frequent code-review question.

When we were unable to get the application stable, I made a renegade effort to eliminate all Observables and replace them with standard, well-understood Promises instead.

The result was fewer lines of code, clearer code, fewer bugs and more tests as bootstrapping/mocking a Promise-based API is significantly less effort than doing the same for its Observable-based counterpart.

It was a terrible experience all over and realistically (given the choice) I’ll never touch an Angular or RxJS based app ever again. My team will just touch React for any new development from now on.

Re:Functional Reactive Programming by spiffytech on Tuesday August 16, 2022 @09:11PM (#32491090)
A friend had the same experience with Rx in Angular. Simple things were crazy complicated, and everything was hard to understand.

He did the same thing: ripped out all the observables and replaced them with Promises and async/await. He loves how much it simplified things.

Re:Functional Reactive Programming by hnthrway on Tuesday August 16, 2022 @01:56PM (#32486371)
>I made a renegade effort to eliminate all Observables and replace them with standard, well-understood Promises instead.

Wow. You would have been better off ditching Angular entirely, or sucking it up.

Re:Functional Reactive Programming by josteink on Tuesday August 16, 2022 @02:35PM (#32486871)
You’re downvoted but entirely right.

Unfortunately I didn’t have the time nor management buy-in for doing that (a full rewrite), so instead I went for the second best option, which was making the code we had at least manageable.

Re:Functional Reactive Programming by zionic on Tuesday August 16, 2022 @09:10AM (#32482264)
I moved everything from RxSwift to Combine back with iOS (13?) and was happy with the experience.

As for the “complexity”, I largely solved this problem by limiting dimensionality of stream handling to 1, and chaining streams via explicit function calls so that I was never map/flat mapping stream to stream to stream etc.

This made everything much easier to understand in my projects, and also test etc.

But you’re right as now that async/await is in Swift proper I’m not sure it makes much sense anymore. There’s still some functionality I need that seems external (looks like async sequence might help once I can use iOS16 as a min spec?). Beyond the basic stuff, it’s essential that I can:

1) Define explicit timeouts that throw typed errors for every step of the async process

2) Have the ability to queue up an array of async operations (each with their own timeouts), dispatch them in a serial queue, then buffer their results to emit a single array, itself with a global timeout

3) While doing all of the above, I need a way to terminate async actions “in flight” upon receipt of some signal, so that a long running async queue can be aborted if necessary

Once all of the above is possible I’ll probably go back and rip out Rx (Combine) in a few years for a pure async-await implementation, but that will take forever

Re:Functional Reactive Programming by manmal on Tuesday August 16, 2022 @09:40AM (#32482569)
AsyncSequence is good to go with iOS 13+ now, you can start using it right away.
Re:Functional Reactive Programming by zionic on Tuesday August 16, 2022 @10:47AM (#32483470)
https://github.com/apple/swift-async-algorithms

Requires Xcode 14, which is still in beta and cannot push to the App Store.

Also, Apple fucked their back port badly. It’s supposedly fixed now, but if you built an app that used async/await anywhere in Xcode 13.X and a user installed running iOS 12/13/up to 14.5 they’d crash on launch.

So I personally wouldn’t trust it, and instead just push to raise your iOS minimum. I’ve had no problems requiring iOS15 in my projects over 1M installs

Re:Functional Reactive Programming by jshier on Tuesday August 16, 2022 @11:15AM (#32483918)
swift-async-algorithms can be used in earlier versions of Xcode if you checkout an earlier tag. They just moved forward more quickly.

Async / await's backport no longer causes crashes in Xcode 13.4, but it does have the downside of being the Swift 5.5 runtime, which has several unfixed issues, unfortunately.

Re:Functional Reactive Programming by andrekandre on Tuesday August 16, 2022 @07:07PM (#32490198)
> 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.

Re:Functional Reactive Programming by jshier on Tuesday August 16, 2022 @10:42PM (#32491685)
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 for me, just something to be aware of.
Re:Functional Reactive Programming by andrekandre on Wednesday August 17, 2022 @08:11PM (#32503542)
got it, thanks, i'll check the forums again!
Re:Functional Reactive Programming by coding123 on Tuesday August 16, 2022 @12:07PM (#32484760)
I have the same experience. I remember when I was first introduced to Rx(JS in my case) it was really fun to compose async flows. I kept having to explain it to others and they would sort of scratch their head. Eventually we stopped using it. A year later I found some code still using it, and to my own amazement, I was having trouble understanding it. Lightbulb. It's like perl - it's write only.
Re:Functional Reactive Programming by christophilus on Tuesday August 16, 2022 @09:31AM (#32482461)
I feel the same way about RxJS. Moving from that to plain React hooks and data flow saved me a ton of sanity on one project a few years back.
Re:Functional Reactive Programming by yen223 on Tuesday August 16, 2022 @07:14PM (#32490271)
Currently working on an Angular codebase. The use of rxjs everywhere is, in my opinion, one of the many things holding Angular back
Re:Functional Reactive Programming by ajuc on Tuesday August 16, 2022 @04:26PM (#32488398)
> Refactoring data-flows that go through multiple modules is a huge hassle. Sometimes, we would spend hours just refactoring data-passing, wrapping and unwrapping and tests surrounding modules, because we needed to pass some additional values.

Same experience. Functional/declarative code is more elegant, but a small change in desired behavior might require completely different structure of the code (or an ugly hack). Meanwhile imperative code changes much less even if it's less elegant.

Re:Functional Reactive Programming by 62951413 on Tuesday August 16, 2022 @12:01PM (#32484656)
That's basically my Akka (akka-streams/Alpakka specifically) experience. When it works it's great and terse. If it occasionally hangs in the middle of things good luck finding that one timeout parameter you need to adjust. Also, not all people who actually use Actors bother to draw a large FSM diagram.
Re:Functional Reactive Programming by disantlor on Tuesday August 16, 2022 @09:48AM (#32482659)
i’ve been writing FRP code in JS for years now. Ease of refactoring and total clarity of what is happening (assuming you are familiar with the FP methods, etc) are the biggest advantages.

I agree the code can balloon in size, and gets a bit hard to organize at some point, but the self-documenting aspect of it is amazing.

I combine an FRP framework (bacon.js) with Ramda.

I’ll admit I’ve had trouble working with things like React because I’m so used to wiring everything up explicitly and there being no “magic” behavior.

The explicitness is probably what makes it seem so unwieldy as the code/team scales

Re:Functional Reactive Programming by cybrox on Tuesday August 16, 2022 @10:00AM (#32482828)
Out of curiosity, a lot of people are praising the "self-documenting aspect of [Reactive Code]", and I definitely see why.

A single block of statements that transform and handle data in a specific way is indeed almost self documenting, which is really cool.

However, how did this work for you with more complex combinations and transformations?

For example, our codebase had one module that took inputs from multiple different sources (low-level network handling based on a library) and generated a consistent state out of them. To keep the code performant, we introduced some intermediary values, so that certain transformations had to be run less frequent. (this was necessary)

In the end, we ended up with a ~500LoC module. I wrote the same later with async/await and split everything into ~20 functions, which worked really well. The reactive version however, was just a bunch (~15) blocks of transformations, which were somewhat self explanatory in themselves but it was almost impossible to trace the flow of data through the whole module without drawing it up.

Even just naming a block (a function name) helped a lot. Sure, you can document blocks of reactive code, which is what we ended up doing but I felt like the self-document aspect fell apart when I had to write a sentence above every block as to why this intermediary transformation is necessary.

Re:Functional Reactive Programming by disantlor on Tuesday August 16, 2022 @10:23AM (#32483138)
For me, I think it comes from the ability to easily split a stream at any point and create intermediate variables. I get pretty obsessive about the variable naming (as I'm sure many do here) but a good variable name is crucial to the self-documenting concept. Also, creating more intermediate values than strictly necessary, creates more opportunity to flesh out the explanatory breadcrumb trail.

Because of the enforced formality of how data is flowing around, the ability to split these streams up is trivial in a way that was a revelation to me. In the past, I'd be far less confident that I was going to mess up something unintentionally, to the point where I would maybe not bother.

And yeah, I think it's totally valid to take larger blocks of streams/transformations and wrap in a named function that takes a stream and returns a stream.

Over the years I've gotten out of the habit of always using FRP for every problem. There are many cases where async/await is totally fine and simple. I still always use Ramda, which gives me the same self-documenting qualities (which is really a function of the strictness of FP, not so much the R in FRP, now that I think about it).

But whenever I have to coordinate multiple inputs with different time delays (i.e. multiple AJAX requests or UI input) or process a bunch of data where each item creates its own stream of fetching and processing, the "reactive" bit is extremely handy.

Re:Functional Reactive Programming by andrekandre on Tuesday August 16, 2022 @07:02PM (#32490146)
i think an issue is that most "apps" are just CRUD and the "reactive" parts end up just being used to propagate the READ part to the ui, but this can be handled by promises most cases (ime) just fine

the other part is the UPDATE from the ui to the model, again this can be handled by simple callbacks and refresh the ui again with promises

there are some nice features of these libraries but most of it goes unused because most apps don't actually need all that power/complexity (sadly i suppose)

lastly, (ime) many devs also don't seem to know how to program data flow in a single direction which gets really hairy because now everything is reacting to everything and fixing one thing breaks another....

Re:Functional Reactive Programming by slowmovintarget on Tuesday August 16, 2022 @05:03PM (#32488885)
Your problem seems to be with static typing, not FRP.
Re:Functional Reactive Programming by cybrox on Wednesday August 17, 2022 @05:40AM (#32494146)
Could you elaborate? I don't see why, aside from the "refactoring dataflows" part but without static typing we would have just missed some of them and introduced runtime errors, which would have been even worse.
Re:Functional Reactive Programming by zmgsabst on Tuesday August 16, 2022 @10:22AM (#32483132)
Do you have any thoughts on what would make a better experience?
Re:Functional Reactive Programming by jfoutz on Tuesday August 16, 2022 @11:30AM (#32484136)
So, I'll start with a disclaimer - I've only futzed around with FRP in Haskell, and never for money. Somewhere on an old computer I've got some code to simulate a pool table. But it has been a few years.

The pool table is pretty fun to model. You've got events, which are collisions, and you've got an evolving state that you can sort of fast-forward through thanks to elementary physics. if you want to sample at a given time, you can. You can calculate out the time to the next event (collision with another ball or bumper)

For me, when I got to multiple objects interacting at different times it got really tough to store the state of the world. (like after the break) I didn't try to support angular momentum - but it would have been fairly straightforward to add.

What I've been thinking about lately, is algebra driven design https://algebradriven.design That'd really help with nailing down the data structures and operations on them. That book has a couple of really gnarly sequenced state with constraints examples, that came from real production code.

There's no closed form solution to some problems (a lot! most problems!). So I think really nailing down a data structure, and providing operations on that data structure are essential. You, well, I need to create an environment where I can create tactics for solving special cases, without that code for the tactic getting smeared all over the code base.

I'm a dilettante, I've got very little experience on the JS side of things. But from pure Haskell, I'm a reasonably sophisticated amateur. So take my opinion in that context.

I think the automated tooling for finding laws presented by Sandy Maguire really have a lot of potential for Locking down those interactions - and opening up new ways of decomposing those interactions, to make more sophisticated descriptions of interactions. I'm not clever enough to find a closed form, but given some state and a time delta, I can work out a bunch of special cases - the interactions. Keeping that tidy is tricky. That algebra approach seems really promising.

Again. I'm a dilettante. Take my opinion in that context. There are probably horrible corners where all this falls down. Anyway, the day is starting and it's almost time to go wrestle yaml.

Re:Functional Reactive Programming by mncharity on Tuesday August 16, 2022 @04:34PM (#32488527)
Having a state algebra can also help with speculative execution, as when doing FRP-like complex event processing on human input device streams, while preserving responsiveness despite diverse latencies.

Consider doubleclick. After a click, rather than pausing for 30 frames to see if a second click eventually shows up, you can act immediately, and if there's later a second click, retract and rerun with a doubleclick event.

With incremental speech recognition, interpretation of past words can change as a sentence progresses, so "Launch the missiles!" can replay as "Lunch is mussels! In butter!".

Keyboard and ml-based visual tracking have very different latencies. So when combining them (eg, which finger pressed the key, where on the keycap, and what were any other vision-derived key modifiers), you can pursue a guessed-at transition immediately, and then correct if needed when tracking finally coughs up the hand pose.

Edit: Hmm. Well... having "retractable state transitions". "Algebra" has other implications. Edit edit: s/reversible/retractable/ - sorry.

Re:Functional Reactive Programming by jfoutz on Tuesday August 16, 2022 @05:44PM (#32489370)
Yeah. Java swing early on actually paused the event loop waiting for a second mouse click to return a double click. If I recall, some platforms would drop those events while paused, others would queue them. I think X dropped, windows didn't. this caused some workarounds smeared across the codebase.

It's never going to be easy, and somebody has to pick the "right" answer. With more structure, you can get more consistency. I may not like the answers, but they're consistent answers and so I can reason about them.

Reversible state transitions is a whole other bag of mixed metaphors. But I'd still say, more structure makes it consistent, and then you can reason about it, and then you can maybe solve some of those cases.

Re:Functional Reactive Programming by cybrox on Wednesday August 17, 2022 @05:49AM (#32494206)
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.