[HN Gopher] Functional Reactive Programming
___________________________________________________________________
Functional Reactive Programming
Author : amelius
Score : 171 points
Date : 2022-08-13 11:18 UTC (3 days ago)
(HTM) web link (wiki.haskell.org)
(TXT) w3m dump (wiki.haskell.org)
| dllthomas wrote:
| There's a lot of talk of Rx libs, but from
| https://reactivex.io/intro.html I see:
|
| _" It is sometimes called "functional reactive programming" but
| this is a misnomer. ReactiveX may be functional, and it may be
| reactive, but "functional reactive programming" is a different
| animal. One main point of difference is that functional reactive
| programming operates on values that change continuously over
| time, while ReactiveX operates on discrete values that are
| emitted over time."_
|
| At the same time, I see lots of references to FRP on
| https://reactivex.io/tutorials.html so I am not sure what the
| actual position of the project is, or the actual status of the
| technologies (maybe things have changed?).
| Paradigma11 wrote:
| There was reactive programming coming from c#. You can look up
| Eric Meijer and the duality of Observable and IEnumerable. It
| is discrete, push and event based and has some history with
| flow based programming.
|
| FRP comes from Conal Elliot and has a continuous pull based
| aspect with maybe an additional reactive event based component
| on the side.
|
| Then the javascript horde discovered rx and since functional
| programming is cool, rp is now frp.
| dllthomas wrote:
| But an article on FRP on haskell.org is probably actually
| about FRP, right?
| Paradigma11 wrote:
| Indeed. That is why you have: at : Behavior a - Time - a
| which is a pull based continuous data type. This is a very
| elegant concept but comes with its own set of difficulties
| if implemented naively (space and time leaks).
|
| A similar concept is shown here:
| https://www.microsoft.com/en-
| us/research/uploads/prod/2000/0...
|
| Imo the main problem in the end is/was that not many
| problems are that conveniently described as continuous.
| Discrete and push based (rx) can do most things and even
| that is overused. It makes problems that are otherwise very
| hard, much easier to describe and solve using the many
| powerful combinators https://www.learnrxjs.io/learn-
| rxjs/operators
|
| On the other hand you make all those easy problems quite a
| bit harder and if you are not disciplined you end up with
| complex rx "queries" that have semantics that you dont
| understand.
| BoumTAC wrote:
| I learn FP throught ELM and I really enjoyed it. Unfortunately
| the language seems dead.
|
| What are the easiest FP language to learn ? I checked multiple
| time Haskell but it seems really hard
| macintux wrote:
| Very different from Elm or Haskell, but Erlang was my gateway
| drug to FP. I'd tried and failed to learn Lisp on a few
| occasions.
|
| Elixir is more popular these days, but I still prefer Erlang's
| syntax. Minimalistic, helps me "think" Erlang instead of
| imperative/OO.
| miggol wrote:
| So sad to hear that Elm appears to be dead. It really excited
| me at first but I guess that like many I never made the time to
| give Elm a serious attempt.
|
| For me, learning Racket was not too hard. The offical learning
| materials are excellent, although they are also meant for
| programmers starting from zero. For me that meant that I
| occasionally skipped over parts I considered "too easy" only to
| be confronted by my hubris later.
|
| For me, what makes lisps easier than Haskells is that lisps are
| multi-paradigm. So you can write a more imperative
| implementation of whatever you're doing right next to the
| "proper" functional one to get things to click, and also to
| identify those elusive merits of functional programming.
|
| I wouldn't describe the Racket community as "vibrantly alive",
| but it's definitely still moving along. And the knowledge is
| very transferrable to other lisps and schemes. Next on my list
| is Carp, for example. And if you (choose to) use Emacs you'll
| reap even more rewards from the knowledge transfer.
| carapace wrote:
| Elm isn't dead. It's in that happy place of stability, people
| are getting things done with it w/o hassle or churn.
|
| I've heard that an Elm-to-native app compiler is in the works
| somewhere, which IMO is very exciting.
| Akronymus wrote:
| Maybe elmish could be of interest to you?
| https://github.com/elmish/elmish
| immigrantheart wrote:
| OCaml fits the bill for me. It is an amazing language, very
| practical, fast, and easy to code with. It is a shame that I
| didn't discover OCaml sooner.
|
| FWIW, I tried Elm, Haskell, PureScript, Elixir.
| tasuki wrote:
| What were some drawbacks of PureScript, or why do you prefer
| OCaml? I've only ever used Elm and Haskell, but been eyeing
| PureScript recently...
| swyx wrote:
| what makes you say Elm is dead?
| sfusato wrote:
| Elixir paved the way into FP for me. I did Lisp, Prolog and a
| bit of Java & Javascript done the FP way (I know) at University
| though, but only with Elixir I've had the "now I get it" Eureka
| moment.
| cardanome wrote:
| Elm is not dead. It just prefers a slow release schedule but is
| still actively worked on in the background.
|
| That said, you might want to check out OCaml for general
| purpose programming. Super fast compiler, great performance,
| can target both native and JS.
|
| It is easier to use than Haskell due to defaulting to eager
| evaluation (like most languages) strategy instead of laziness
| and being generally more pragmatic, offering more escape
| hatches into the imperative world if need be. Plus great upward
| trajectory with lot's of cool stuff like an effects system and
| multi-core support coming.
|
| Real World Ocaml is a decent resource:
| https://dev.realworldocaml.org/
| jamil7 wrote:
| > you might want to check out OCaml for general purpose
| programming
|
| Any tips on backend frameworks to look at? I need to write a
| small websocket service for a side-project and have always
| wanted to try OCaml. I came across
| https://github.com/aantron/dream.
| phosphorco wrote:
| You'll have a much easier time doing business stuff with F#
| - it's OCAML inspired but has access to broader .NET
| ecosystem. We're using it on front end as well w/ Fable
| compiler. It's pretty awesome.
| pkilgore wrote:
| Dream is great for small http services -- I've not used the
| websocket support so I can't say much about it.
|
| I do highly recommend starting with one of the Dream
| example projects just to lower the barrier to entry on the
| tooling side.
|
| You can also use Rescript (compiles to very readable
| Javascript), which is the OCaml type system with more
| familiar syntax and some of the complexity shaved off.
| airza wrote:
| I have been using rxjs with typescript for some stuff.
| Functional reactive has proven to be a good paradigm for some
| stuff in the UI and thornier parts of shared state across
| multiple applications.
| valenterry wrote:
| Proper FP as in pure functional programming?
|
| Probably Purescript in the frontend (also hard) or Scala on the
| backend (easier, because you can also write impure code).
| smcl wrote:
| F# might be a good shout.
| quickthrower2 wrote:
| There is the SAFE stack https://safe-
| stack.github.io/docs/overview/ where the E means Elmish
| rstarast wrote:
| I have high hopes for the young Gren fork of Elm https://gren-
| lang.org/.
| brabel wrote:
| 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?
| yakshaving_jgt wrote:
| > Elm has always preferred purity over practicality
|
| Elm preferring purity _is_ its practicality.
| rstarast wrote:
| 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 open development model
| (even just not having to wait years to fix trivial
| crippling bugs in the compiler is a step forward...).
|
| The other part is that I have a good impression of the
| person/people behind it and could see it sticking, for
| whatever that's worth.
| cosmic_quanta wrote:
| 'Haskell Programming from First Principles' is a great book
| which helped me a lot in the beginning.
| wyager wrote:
| Haskell is sort of the base model option if you want real full-
| power FP, and not just support for some FP techniques.
| giovannibonetti wrote:
| Elm started with FRP and moved on to Model-View-Update, which I
| agree is way simpler to understand and work with.
|
| https://elm-lang.org/news/farewell-to-frp
| rkangel wrote:
| I would recommend starting with Elixir. Elixir is a _dynamic_
| functional language, unlike Haskell /Ocaml etc. which are
| statically compiled languages.
|
| This means that you can learnt he basic concepts of operating
| on data with pure functions, pattern matching etc. Plus it's a
| nice language that is great for building a lot of things
| (Phoenix is a great web framework).
|
| Once you've got some of that background of how you build
| functional programs rather than OO, moving to something like
| Haskell or Ocaml is a bit easier because it becomes more about
| understanding how to declare types to keep the compiler happy
| and less about the fundamental program design stuff.
| aporetics wrote:
| For those of you interested in hearing the inventor of FRP talk
| about it, take a look at Conal Elliott's retrospective talk in
| 2015:
|
| https://youtu.be/j3Q32brCUAI
|
| Especially useful for those whose only exposure to FRP is through
| frp-inspired libraries (they're not the same thing).
| 725686 wrote:
| This might be interesting from a CS perspective, but useless to
| developers who have to create real life messy business
| applications.
| aporetics wrote:
| As a developer of real life messy code (as opposed to unreal
| life?), I have never found ignoring CS to be beneficial; and
| on the other hand, deciding in advance that CS is not helpful
| is hubristic: you never know when theoretical insight will
| become actionable.
|
| On the other hand, who wouldn't be curious? Like someone who
| purports to enjoy fast cars but has no interest in
| understanding internal combustion or electric engines?
| dfee wrote:
| 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 dataloaders.
|
| The Reddit almost encourages people to use a different language,
| too.
| _query wrote:
| Yes, it's possible to build a traditional web company with
| Haskell. We've made IHP exactly for that :) It's like
| Rails/Django but for Haskell. https://ihp.digitallyinduced.com/
| We specifically try to be batteries-includes (like rails), so
| you don't have to think too much about what libraries to use,
| the core of IHP can get you very far without needing to
| manually decide between libraries.
|
| IHP even won a G2 badge, which is kind of funny and ironic for
| a Haskell project :D https://www.g2.com/products/ihp/reviews
| hiptobecubic wrote:
| 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 and instead is just
| a bunch of building blocks. Yeah you can write great stuff in
| Haskell, but also you have no choice. No one else has written
| what you need so you'll be writing it yourself.
|
| The benefits of Haskell don't even _really_ seem that useful
| when you're just writing yet another big ass crud app where the
| main goal is to convert json into some other json and then
| maybe render it. I could see it making more sense in specific
| domains where the main challenge is wrangling a complex mess of
| business logic into something you can be confident about. Not
| like "is my shopping cart correct" but "how can I optimize this
| PCB design to reduce trace length and total area?"
| yakshaving_jgt wrote:
| > The benefits of Haskell don't even _really_ seem that
| useful when you're just writing yet another big ass crud app
| where the main goal is to convert json into some other json
| and then maybe render it.
|
| The benefits have been massive for me, and I'm often doing
| basically this.
|
| Maybe you should just try it?
| JoelMcCracken wrote:
| You absolutely can. Its a great platform for this.
|
| There may not be as wide a variety of libraries as you would
| hope, but that's true of any smaller language.
|
| The main thing you need to be able to do is have the wisdom to
| avoid the footguns. They're quite different from the footguns
| in other languages. in general, using Haskell well requires a
| good bit of experience with it, and knowledge of who in the
| community are selling false solutions.
| mdcds wrote:
| In 2015-2016 SF Haskell meetup used to be hosted by an ed-tech
| company that used Haskell as the main language, so maybe
| viable.
| yakshaving_jgt wrote:
| > Is it viable to build a traditional web product/company on
| top of Haskell?
|
| Yes. I've been doing it for years. I also employ several
| Haskell developers.
|
| We're happy.
|
| > I'm not sure if there's enough tooling or libraries to get to
| market.
|
| There are.
| endgame wrote:
| FRP is a fascinating paradigm, but I find I really have to turn
| my brain inside-out to "get" it. But it's really cool to have UIs
| that are completely consistent.
| mpweiher wrote:
| The funny thing is that good ole MVC also gave us UIs that are
| completely consistent.
| valenterry wrote:
| Not really. It's just that at the time, SPAs and concurrent
| requests were not really a thing or only used rarely.
| mpweiher wrote:
| Yeah, actually really.
|
| _Actual_ MVC has the model notifying the UI that it has
| changed and then the UI updating itself from the model, by
| pulling data. That 's always consistent.
|
| We then did all sorts of things that we _call_ MVC, but
| that do not follow the MVC-defined interaction patterns at
| all, particularly either the model or other views
| incrementally poking data into the view.
|
| _That_ doesn 't work, and it is nigh impossible to keep
| consistent. It also isn't MVC.
|
| I haven't written this up stand-alone, but I talk about it
| a little here:
| https://blog.metaobject.com/2022/06/blackbird-simple-
| referen...
| valenterry wrote:
| So, what if you have a background process (running in the
| controller I assume) that updates data in the model
| periodically from somewhere. And at the same time you
| have the user making changes to the model. How is
| consistency guaranteed here by having "UI updating itself
| from the model, by pulling data"?
|
| Maybe I have a different understanding of "consistency"
| but this might very well lead to undesired results if the
| logic of data updates isn't well-controlled. The
| developer needs to decide if the updates can happen
| arbitrarily or if some kind of transaction-model needs to
| be used, forcing the background process to wait during
| user interactions or the other way around, etc.
|
| I don't see how MVC solves that problem.
| mpweiher wrote:
| This is not MVC's problem to solve. It also doesn't solve
| global warming.
|
| If you have unprotected multithreaded imperative updates
| of global data, _nothing_ is consistent. Has nothing to
| do with MVC or no MVC.
|
| Actually having a consistent state to present to the UI
| is the model's problem.
|
| Oh, and for goodness sake, don't have any kind of async
| update process running in the controller. All this stuff
| belongs in the model.
|
| If you're doing that sort of stuff in the controller, I
| can see why you're having problems with concurrency.
| You're also almost certainly not doing MVC.
| valenterry wrote:
| > If you have unprotected multithreaded imperative
| updates of global data, nothing is consistent.
|
| Yeah, but that is exactly what FRP solves (or strives to
| solve) and MVC does not give you on it's own (as you
| said). Ofc MVC can be used in combination with other
| techniques to gain consistency, but it doesn't provide it
| on its own, which is what I believe was implied by your
| original post.
|
| If you were just talking about the UI in isolation, then
| yeah, maybe MVC gives you that, but it misses the point
| of what FRP gives you.
| mpweiher wrote:
| > If you were just talking about the UI in isolation
|
| Comment I was replying to:
|
| >> But it's really cool to have UIs that are completely
| consistent.
|
| So yes, we kinda were talking about the UI, keeping that
| consistent (with the model, presumably). Hence MVC.
|
| Keeping the model consistent is another topic.
| valenterry wrote:
| I see - I guess the OP had something different in mind
| when they meant UI, not just the pure View. But point
| taken, I understand your response.
| cybrox wrote:
| You can build perfectly fine SPAs using an MVC
| architecture, even with lots of concurrent requests and
| data fetching. This is why JS has an event loop and why we
| invented data-binding.
| wiseowise wrote:
| You can build perfectly fine SPAs in one big file, what's
| your point?
| klysm wrote:
| Yeah but for a limited subset of desirable functionality. It
| sucks to use for something where you want a dynamic UI.
| mola wrote:
| Can you elaborate? An example for a feature which would be
| a real pain in MVC but easypeasy with FRP?
| klysm wrote:
| I'm not saying FRP makes anything easy, just that there's
| a set of things that are desirable that MVC makes
| difficult. Right now I'm making a graphical node editor
| with a lot of drag and drop that displays live data
| streams. I can go into more detail about why a typical
| MVC sucks for this but it definitely does.
| daxvena wrote:
| Another funny thing is that good ole vacuum tube computers
| could automate computation before transistors became
| widespread.
| valcron1000 wrote:
| I disagree. The moment my controller needs to "set" or
| "update" something in the model the whole thing becomes a
| mess in my experience. FRP requires that you change the way
| you code but it's 100% worth IMO.
| mpweiher wrote:
| Well, you can let the view do this and things get better
| ;-)
| cybrox wrote:
| 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.
| 62951413 wrote:
| 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.
| zmgsabst wrote:
| Do you have any thoughts on what would make a better
| experience?
| jfoutz wrote:
| 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.
| mncharity wrote:
| 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.
| jfoutz wrote:
| 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.
| amarant wrote:
| 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.
| cybrox wrote:
| 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.
| twawaaay wrote:
| 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.
| disantlor wrote:
| 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
| cybrox wrote:
| 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.
| disantlor wrote:
| 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.
| coding123 wrote:
| 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.
| christophilus wrote:
| 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.
| marcosdumay wrote:
| 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.
| slowmovintarget wrote:
| Your problem seems to be with static typing, not FRP.
| josteink wrote:
| > 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.
| hnthrway wrote:
| >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.
| josteink wrote:
| 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.
| ajuc wrote:
| > 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.
| zionic wrote:
| 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
| manmal wrote:
| AsyncSequence is good to go with iOS 13+ now, you can start
| using it right away.
| zionic wrote:
| 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
| jshier wrote:
| 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.
| ghuntley wrote:
| See also https://reflex-frp.org/
| faleidel wrote:
| 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 to work with.
|
| With promises you have a few functions with which you can do
| everything. With rxJs you have dozens of function with specific
| use cases and most of them looks alike. It's too easy to not take
| the right one and new peoples in the project needs to learn a lot
| of things to understand the codebase.
|
| I was interviewing some angular devs aand asked the question:
| what is the difference between a promise and an observable and
| 80% of the time the answer was "for observable you need to
| sunscribe to get the result"). That shows a clear lack of
| understand of rxJs.
|
| Anybody had a better experience with angular and rxJs?
| dhucerbin wrote:
| 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 produce state as a
| stream result or you put some `tap` or `subscribe` with
| `takeUntil`.
|
| But rx.js shines with more complex user interactions - drag and
| drops, brushes, interactive forms (angular has nice, reactive
| form api). We even put some state machines inside streams, so
| input signals produced events. and we had `scan` operator that
| manipulated machine.
|
| My personal issue with rx.js is that BehaviourSubject is leaky
| - you can do anything to it, and break it's property of 'always
| has a value'. It is nice if your service is a reactive value
| that you can inject and transform or render using async pipe.
| But you need to be careful what operators are you using on it.
| pfooti wrote:
| On the one hand, I absolutely love working with rxjs on a
| fairly large angular app as a side project. It affords me the
| chance to be clever with code, to reason about coordinating
| multiple asychronous streams, and so on. Firestore gives me
| observables of the queries I run where the data update
| themselves, and the user generates events that turn into data
| mutations. It is all just super great.
|
| On the other hand, I recognize that doing this well and
| correctly (understanding how to pipe together operators instead
| of creating lots of intermediate subscriptions manually, which
| I see a lot in example / stack overflow code) requires a high
| level of understanding.
|
| It is a whole change in how you think about event pipelines and
| code structure. I don't think I would want to migrate my day
| job systems to it, because then you need everyone on the team
| to develop that understanding. I'm sure they _could_, but
| working with promises and trad event handlers is a lot simpler,
| as long as you keep the rest of the data / eventing pipelines
| simple.
| jinder wrote:
| Yes - totally the opposite. In fact, I think the issue with
| Angular is that they've not finished the work to make Angular
| RX-everywhere (for example, reactive components need to be
| manually plugged into Subjects at the moment).
|
| With RxJS you should use it everywhere (observing state,
| observing component inputs, side effects etc). It's when you
| use it half-heartedly that you get problems with merging
| different programming paradigms.
|
| The biggest issue with RxJS that we've found is that some devs
| have a super hard time getting to grips with the paradigm, and
| if your project is mostly those types of people, it will end up
| a disaster.
| polyterative wrote:
| > With RxJS you should use it everywhere (observing state,
| observing component inputs, side effects etc). It's when you
| use it half-heartedly that you get problems with merging
| different programming paradigms
|
| Yes, yes ,yes! I have been working with angular
| professionally for five years and fell in love with rxjs. If
| you manage to use it for everything, it really shines. Your
| entire codebase becomes declarative and it works beautifully.
| The only downside is that it takes some time to get it
| started up from the ground up, but once you do making changes
| and adding features becomes trivial. Try making smallish
| pipes and comment their purpose. Break them modular pieces.
| cphoover wrote:
| I understand this paradigm within the context of stream
| processing, but it seems like a weird way of modeling most REST
| api-based web applications.
|
| I feel like a promise-based model makes way more sense for most
| simple web applications. Where you have a http request and
| response, a promise model seems to suffice for most
| applications communicating via http-requests to an API layer.
| Modeling as a stream doesn't make sense to me, and seems like
| it would over-complicate an otherwise simple mental-model.
|
| In most of the simple web-applications I have encountered there
| is one or more requests made to unique endpoints for data after
| the document response has been completed. No need for handling
| multiple events from the same endpoint, debouncing, multi-
| casting, unsubscribing, back-pressure, or whatever else. These
| operations seem to make way more sense in the context of stream
| processing.
| mrcartmeneses wrote:
| Yeah totally the opposite. I really like RxJs and used it along
| side my senior dev for good effect at my last perm for several
| years. Our junior did find it a bit tricky sometimes though
| (edit: I don't mean to imply you weren't "senior" enough, just
| an observation that it was a new set of concepts to learn).
|
| We would literally never use promises because we'd become so
| comfortable with how we (and angular) managed the lifecycle of
| observables. And we never ever used a async/await after some
| nasty bugs in previous projects. We never started making our
| own operators. And I think we never really got too complex with
| it. RxJs marbles and learnrxjs were often consulted. But the
| code seemed nice and clean and reliable. Testing was more
| difficult and harder to understand but we got there.
|
| We switched to using NgRx for our state quite early in the
| project after a brief flirtation with observable services, so
| that probably pushed us further down the observable route. We
| kept NgRx up to date and found the helper functions really nice
| so there wasn't too much boilerplate. Making new selectors and
| integrating them into components with the async pipe is just so
| damn easy with NgRx and RxJs. Effects would get mildly stupid
| in terms of complexity and I did have a habit of hilariously
| writing "these were your father's parentheses" at the end of
| any particularly long set of closing parens... but yeah it
| seemed to just work and give us relatively few bugs and none
| that I recall were hard to track down. It was all very smooth.
|
| The only thing I'd ever really complain about on that project
| other than our build times was MSAL, which I hated with a
| passion.
| coding123 wrote:
| Rx is a hot-shot, look what I can do, I am smarter than you,
| paradigm and not much more. Source: I did that to co-workers
| when I learned it.
| revskill wrote:
| FRP is simply the true way to build real time application.
| marcosdumay wrote:
| You mean interactive? I don't see how FRP helps with real time.
| dllthomas wrote:
| I expect the parent means having some portions of the output
| change without user input, which isn't either of those if
| we're being picky.
| marcosdumay wrote:
| 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.
| dllthomas wrote:
| "Discerning"? :)
| vladsanchez wrote:
| As much as I'd love to learn Haskell, Lisp, OCaml or F#, try
| https://reactivex.io/ and pick your language.
| kgr wrote:
| This short fun video demonstrates how FRP is related/similar to
| spreadsheets:
|
| https://www.youtube.com/watch?v=-fbq-_H6Lf4
| InflexHQ wrote:
| 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. your program gets pushed
| to, by some other active agent.
|
| The trick is how one "makes things happen". If you want to pull
| the latest tweets every 5 seconds, in reverse order, you might
| have code that looks like:
|
| timer(5).joinWith(latestTweets).map(reverse)
|
| Similar to promises you're always building up more declarative
| values. It's just that FRP has a clean semantic description.
| There are laws, and no assumptions about time/imperative escape
| hatches. It can be quite hard to use it practically for some
| types of apps; space leaks and cycles are a challenge, and some
| code can be messy. Research continues.
|
| But for my simpler use-case, it's a very good fit. Spreadsheets
| use "volatile" cells to side-step the whole issue of interacting
| with time and the outside world, but it leaves a bad taste in the
| mouth because it's a hack that makes state implicit. FRP brings a
| strong mental framework to address this properly, rather than as
| an afterthought.
| shinzui wrote:
| I got the impression that you're already using FRP for inflex.
___________________________________________________________________
(page generated 2022-08-16 23:01 UTC)