8

I've been hearing a lot about functional reactive programming, and decided to check out what the big deal is. Going through the bacon.js documentation, it seems that the main difference is that instead of setting an event listener on a component, I create an event stream on it, and pass the event handler into the stream instead. In other words, all I really did was move the event handler from the component to the event stream. Is that it? If so, what's the big advantage of doing this?

tldr
  • 10,743
  • 11
  • 69
  • 111
  • 1
    Well, the examples on the [landing page](http://baconjs.github.io/) seem far more declarative and robust to me. Instead of writing code which listens for `keydown` in `#text` and registering and cancelling timeouts to ignore input followed by more input within 300 ms, and maintaining a list of recent inputs to not send unnecessary requests to the server, you just specify: I want keydown events with a 300 ms pause afterwards, but no duplicates please. The contrast gets even stronger with more complicated logic. –  May 24 '14 at 18:35

2 Answers2

14

The key point about functional reactive programming (FRP) is a syntactic property:

The dynamical behavior of a value is specified at declaration time.

For instance, consider a counter that can be counted up or down by pressing a button. In an imperative style, you would probably write it like this:

counter := 0                               -- initial value
on buttonUp   = (counter := counter + 1)   -- change it later
on buttonDown = (counter := counter - 1)

First, the counter is declared with an initial value. Then, in later parts of the code, you change the value. Now, if someone asks you the question "At any given moment in time, what is the value of counter?", you have to look at all parts of the code that reference the name counter, because that's where it could be changed.

In contrast, when using FRP style code, the question can be answered by looking at exactly one location in the code: the place where counter is declared. For instance, in Haskell, you can write the counter as

counter :: Behavior Int
counter = accumulate ($) 0
            (fmap (+1) buttonUp
             `union` fmap (subtract 1) buttonDown)

The right-hand side contains all the information you need to know what the value of counter is at any given moment in time. In particular, you see that it can be changes by a buttonUp and a buttonDown, and that's it. You don't have to sift through your code, looking for places where the counter might change, no, it is enough to look at its declaration and follow up from there.

This is the reason why FRP code is less bug-prone than event-based spaghetti code.

See also some preliminary documentation for my threepenny-gui library.

Heinrich Apfelmus
  • 11,039
  • 1
  • 35
  • 66
  • 1
    "What is the value of ```counter```?, you have to look at all parts of the code that reference the name counter, because that's where it could be changed." -- Except when you don't declare ```counter``` as some global, but instead encapsulate it in some object with a well-defined interface. That way, a simple ```counter.getValue()``` might very reliably do the trick... – rodrigo-silveira Nov 13 '14 at 19:59
  • 3
    @rodrigo-silveira What I mean is that in order to know what `counter.getValue()` returns, you have to find all occurrences of `counter.setValue(…)` in your code. In contrast, with FRP, these occurrences are all reachable from the declaration of `counter`. – Heinrich Apfelmus Nov 14 '14 at 10:46
  • so the point is 1.) being able to express a changing Value in one Place, right? 2.) to enable that, this "one Place" must have access for all the related information the Value depends on -> so that Place where you reach for the information must be list like (stream) so it can carry different types of information pieces (events). 3.) Assuming we have lists (streams) we can also manipulate them on higher levels (check @Bergi 's answer) , but it's not the main purpose or original intent rather a useful coincidence and feature which this setup enables. Or is it a main feature? – PEZO Jan 11 '20 at 14:09
8

Is that it?

No. It's about having event streams. You still will attach listener to them in the end to execute effects, but between the source and the destination you've got a very mighty abstraction.

what's the big advantage of doing this?

The event streams do have lots of higher-order functions to easily deal with them, and for composing them without writing out all the error-prone boilerplate code.

As the docs put it quite nicely, bacon

turns your event spaghetti into clean and declarative feng shui bacon, by switching from imperative to functional. It's like replacing nested for-loops with functional programming concepts like map and filter. Stop working on individual events and work with event streams instead. Combine your data with merge and combine [and wield] heavier weapons [like] flatMap and combineTemplate.

ocodo
  • 27,324
  • 15
  • 97
  • 113
Bergi
  • 513,640
  • 108
  • 821
  • 1,164
  • so the advantage is that doing transformations on event streams keeps the code cleaner than nesting event handlers? – tldr May 24 '14 at 19:10
  • Yes. With all the advantages of clean code: comprehensibility, readability, buglessness, conciseness. With the well-defined semantics of frp (implemented in the lib) it's also possible to reason about the code and proof correctness. – Bergi May 24 '14 at 19:33
  • 2
    And it's not only transformations (like simple `filter`/`map`/`reduce` operations), but also *composability*, which is the key to functional programming. – Bergi May 24 '14 at 19:35
  • Promises also solve the spagetti code problem. The example at their landing page is very basic and looks more verbose than the same simple functionality done with AngularJS. Is there any real example showing better and cleaner code than Promise/Angular approach? – Dmitri Zaitsev May 25 '14 at 03:15
  • Yes, it's exactly the same spaghetti problem. There is only a small semantic difference between Promises and Properties: Promises wrap a single, asynchronous success/fail value to which they *resolve*, while Properties *change* over time, i.e. get a new value more than once. You will often use them together. I can't tell you the exact advantages over Angular because I haven't really understood their philosophy, but from my POV Angular looks too imperative, automagically propagating *assignments* (well, most of the time at least, there seem to be some inconsistencies). – Bergi May 25 '14 at 15:28
  • I really like that answer, but it does not addresses the "syntactic property: The dynamical behavior of a value is specified at declaration time." as @Heinrich Apfelmus ' answer. I am wondering if that higher order manipulation of streams is the real point of the paradigm or it's just a nice property to have and use but the main point is really the syntactic property. Can you please comment on that? Also, is there a consensus or definition on that question? If so can you please give a reference to it? – PEZO Jan 11 '20 at 14:16
  • 1
    @PEZO It's mentioned in the quote: clean and declarative functional programming. Sure, I might not have it put as eloquently as Heinrich Apfelmus, but it really means the same thing. Instead of stealing his words, I just upvoted his answer :-) No, there is no authority that could give a one and true definition for the term FRP. – Bergi Jan 11 '20 at 14:40
  • Yeah I see then, however "clean and declarative functional programming" was a bit too vague for me and many things claim description. :) Thanks for the clarification though, now I see your point! – PEZO Jan 11 '20 at 16:02