That was where everything went haywire for me - my brain doesn't want to think about those UI elements as streams and I instantly tune out and think "this doesn't make sense". I'm curious if anyone else also got stuck at that same part, as well as if anyone has suggestions/ideas for breaking past this mental barrier? Is there a different way of solving the same problem (that might make more sense to me)?
Similarly, a UI element can be thought of a a stream of snapshots. Each time you render the UI element, you're creating another frame.
In Elm, an entire UI is a stream of frames. (The type is Signal Element.) When you put Elements together into a tree, you're just constructing a single frame of an animation.
Unfortunately, a UI in Elm is not composable in the same way as a traditional UI. The model, appearance, and behavior of a UI element are handled by different parts of the program. This is sometimes good (model and appearance are cleanly separated) but it's unclear whether we can build large applications this way. It's one of the reasons why Elm is still an experimental language.
See eg. https://mollyrocket.com/861
edit: and of course I was hardly the first one to make the connection :) Long IMGUI discussion at http://lambda-the-ultimate.org/node/4561 with 20+ mentions of FRP.
The UI elements themselves aren't streams. Also, you're missing an important keyword--they're "event" streams. Stream in this context just means an ordered set of momentary events occurring in more or less real time--a button was clicked, the server responded, a sensor reading was taken, an item was purchased, etc.
In order to get a good understanding of FRP, it would probably first help to understand laziness as found in Haskell or .NET IEnumerable/yield return or Python's generators, etc. The gist is that you have a function that can return a value and then effectively pause it's execution until you call it again. A simple example is an infinite counter: the first time you call it, it returns 1; the next time it remembers that it had previously returned 1 and so it adds one more and returns two, etc.
Usually when this kind of laziness is considered, it's the job of the calling code to say, "I'm ready for more data" and then 'pull' the next chunk. In FRP, this is reversed--the client/calling code says, "let me know if/when anything interesting happens" and the stream 'pushes' data to the subscriber. The functional part comes into play when you start manipulating the pulled or pushed data by specifying functions as arguments to the 'higher-ordered' functions that work on the data. I.e., when we get the data, do this() to it and then do that()--where 'this' and 'that' are variables/function-pointers.
I think the best tutorial for me would have been an article explaining what is going on under the hood. This tutorial does mention this implementation detail but only very briefly.
Anyway, without the tutorial, I wouldn't have realized this, so it was actually helpful. :)
Often I like to use past participles as names for stream or signal combinators.
So instead of `map`, `filter`, `fold` -- the names are `mapped`, `filter`, `folded`.
For example, given a stream, `(mapped stream f)` returns a new stream with f mapped over it. It returns a mapped stream.
clickStream.map(f) doesn't "do" anything - it takes a stream, and returns another stream that's the result of f applied to each element of clickStream.
So if I do something like:
astream = clickStream.map(a)
bstream = clickStream.map(b)
cstream = clickStream.map(c)
I've created three streams that are each the result of a particular function applied to the elements of an original stream.
Often, you can treat the stream element as an implementation detail. Where possible, you simply define the state of widget A to be a function of the state of widget B. Keeping in mind that this is implemented in terms of streams will tell you what function to use to make this specification. In this line of thinking, I often think of the system as a static network, where each node (representing a widget) is a pure function converting some inputs to output (or has no input and produce a constant output). A widget can also set its appearence based on its input.
When the user provides input to a widget (in this model), the corresponding node itself changes to produce a different output (giving you a new, static, graph). For simplicity, I will assume that all user-input widgets correspond to output only nodes.
For example, consider a netwwork, X, with node A corresponding to a textbox. A takes no input, and outputs the constant of the current value of the textbox. Imagine that we know what the value of the textbox will be at all times, and want to animate the entire window. To do this, we can stream in the values of the textbox, and let the network update itself. In this way the textbox is sending a stream of strings into the rest of the network.
Moving to a lower level, consider how the textbox itself is implemented. While we can think of it as output only (as I almost always do), it can be implemented as taking a stream of key-events, and outputing a stream of strings. At this level, I take an event driven perspective. While the textbox recieves a 'stream' of key-events, and outputs a 'stream' of strings, what really happens is that whenever there is a key event, the textbox receives it, and emits a string event, in the same way that you might have an onEvent() handler call onStringChange() in a traditioanl OO design.
In writing this, and re-reading your question, I think the key difference is that I do not view UI elements as a stream. Instead they are static object that streams pass through. As streams pass through them, they be update the stream or themselves.
Notably, the single-click stream has a built-in 250ms delay before the click is fired. That's bad. Similarly, the double-click has a built-in 250ms delay even if the user clicks twice within 100ms. In fact, it's not just a 250ms delay from the first click; it's a 250ms delay from the last click before it finally emits the event.
That's a completely unusable way of doing clicks. If that's how you do clicks in FRP, then that's a serious issue with FRP. I have to assume there's a better way, and I'd really like to see it.
The correct behavior is to emit a click event immediately upon a click, but to remember recent history and associate a click count with the event based on whether there are recent clicks. So if I click 7 times in a row at sufficient speed, I should get a click event for every single one, getting 1x through 6x clicks before finally getting the 7x click on the last one.
That said, this is not the same thing as a double-click handler. This is a single click plus a double click when you click twice. Typically when you do double-click handlers you delay single-click until it's a confirmed not-double-click. I think I have an idea of how to make that work here, but do you have a preferred approach?
So more than 4 lines of code, but I'm glad to see it's quite doable.
Blog post with screencast to demonstrate:
2048 clone written in a reactive style:
Reading the explanation from the Elm folks is what made things click for me:
Edit: Also, SICP has 2 sections that describe systems that are reminiscent of reactive programming systems.
A Simulator for Digital Circuits:
Propagation of Constraints:
FRP is a development based on the work of conal elliott:
It's main characteristic is that there is a continuous (pull)aspect. Since many interesting domains are not predominantly continuous he and many others had to add a discrete event based component.
Rx is a stream based technology (push). Basically it is a dataflow library with modern functional programming syntactic elements. It does not have a continuous aspect and as such it is not FRP.
By treating Rx as something new we are depriving our self of the dataflow and structured programming knowledge.
Are there other interesting approaches?
Constraint and logical systems like Bloom also qualify in dealing with change (the underlying meaning if "react"). The nice thing about FRP was that it went way beyond event streams, which is why Rx is so disappointing (which is just about event streams, react + Rx is sort of FRP).
At the end of the day it always feels like fighting between general purpose programming languages. Nobody can win, everyone optimizes for different things, and broad terms are useless except for marketing.
From my perspective it is causing confusion...and I don't think I am alone.
I would disagree as being the primary author of RxJS as well as a contributor to all other flavors that it's just about slinging around events. It's more than just that as we have to deal with failure, load, etc.
A.combineLatest(B).map((a,b) => a + b)
A.combineLatest(B).map((a,b) => a + b)
All that matters is the underlying abstractions are Observable (the producer, characterized by its subscribe method), the Observer (the consumer, which is really a single function split in 3) and the communication protocol between them. I don't see that as a regression, I see it as the foundation - in the end, no matter what you make of behaviors, it's still a producer/consumer pattern.
Actually my problem with the original Rx implementation is very different. I'm also working on an Rx redesigned implementation for Scala, with back-pressure baked in by design in the protocol . This is because when events are passing asynchronous boundaries, you can have streams that are producing more data than consumers can process and (compared with other abstractions for processing streams of data, like Iteratees) the consumer doesn't signal demand back to the producer. And this issue becomes even more relevant when events are sent over the network.
We encourage since it is the same stream to use zip to avoid glitches and keep our overall memory footprint low:
We then updated it to have .pause and .resume methods on the Observable itself to take care of some of the ugliness:
Overall, these do not go ultimately up to the publisher as we're dealing with multicast streams in which we don't want other subscribers to pay any penalty for one slow subscriber.
For something to be called FRP, they at least need both continuous and discrete abstractions; the simplest description of FRP involves re-evaluation A + B over time, which is not meaningful in Rx or Elm, really.
In Elm behaviors and events are the same thing. Usually operations like filter and fold are only on events, and applicative functor and monadic operations are only on behaviors, but in Elm one type support filter and fold and the applicative functor operations, and monadic bind is not supported at all. I don't think that's good design, but it does mean that Elm has the functionality of both events and behaviors.
Warning: FRP is much more purest than Rx (which is just data flow) or Elm (which is more FRPish but asynchronous). You probably don't want FRP but something more pragmatic like the fore mentioned systems.
There are some nice papers that give an overview of the FRP landscape.
“A Survey on Reactive Programming”: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.416...
“Towards Reactive Programming for Object-oriented Applications”: http://www.guidosalvaneschi.com/attachments/papers/2014_Towa...
Outside, FRP still means FRP, and using the term will automatically put your work in a certain specific bucket that you might not want to be in.
Aside from using Visual Studio-esque "find references" to navigate to all of the locations in a codebase where a particular event stream is subscribed to, I've yet to come across much.
Is the 'F' in FRP the same 'F' as when people say that, for example, Haskell and Clojure are FP languages?
Does FRP use the notion of "functions" which are closer to the mathematical definition of a function? For example does FRP try to avoid state and mutable data as much as possible and do the same inputs always lead to the same output? Or is it called "functional" simply because it allows to use things like map/reduce/filter?
If it is "really" functional, I take it it's quite deterministic: can you then take all these streams of events and "replay" them at will, always ending up in the same intermediary state and, eventually, in the same final "state"?
If that's the case, then it's the next thing on my "to learn" list :)
This comment https://gist.github.com/staltz/868e7e9bc2a7b8c1f754#comment-... and this discussion https://twitter.com/ReactiveX/status/483625917491970048
> If it is "really" functional, I take it it's quite deterministic: can you then take all these streams of events and "replay" them at will, always ending up in the same intermediary state and, eventually, in the same final "state"?
Yes. See this http://debug.elm-lang.org/
If you are confused by the term FRP and its different interpretations, I tried to summarize them in a stackoverflow answer: http://stackoverflow.com/questions/22795062/is-it-possible-t...
* responsive (somewhat hand-wavy - basically, don't block on IO)
* scalable (hard to be scalable - many different approaches)
* resilient (hard to be resilient - erlang is king here)
* event-driven (it's hard to not be event-driven these days)
We cover that a bit in our repository with the Reactive Manifesto: https://github.com/Reactive-Extensions/RxJS
And I've even covered it at StrangeLoop 2013:
As well as FutureJS:
The fact that you can only return a single value from a promise is a feature, not a limitation. In synchronous code, you can only return once from a function. In asynchronous promise code, you can only fulfill the promise once. There's a parallel.
If you want to "allow many returned values" you can just use a callback. You hardly need a "stream" for that.
Erm, have you heard a term "continuation" or "call/cc" ("call-with-curent-continuation")? Or "delimited continuations"? Or maybe even "Cont monad"? You should read on them, if you haven't - they are one of the most fundamental and fun concepts I encountered.
But even without those, what you wrote here is false in the presence of generators (which are coming to JS any day now!).
I'd say that returning more than once from function is actually a pretty basic technique which is widely used to implement quite a wide array of different things.
>If you want to "allow many returned values" you can just use a callback. You hardly need a "stream" for that.
Callbacks are hard to compose, may not have standard error conventions, and you can't have multiple listeners subscribe to the same thing after the fact. (Those are the same things that promises solve for async functions with a single result.) Additionally, you can stop listening to a stream and the source can know to close itself once it has no more listeners.
https://github.com/fdecampredon/react-rxjs-todomvc (TodoMVC implementation built on top of React + RxJS)
https://github.com/eliseumds/react-autocomplete (React + RxJS Autocomplete)
In addition, we ship a good number of samples within our RxJS repository:
Just a warrning. Node.js had 3 versions of streams so far and some packages that implement stream functionality don't all work properly with latest version. Also two types bytestream and object stream.
The real trick is working with historical events and fetch them from disk, otherwise your reasoning time window is restricted to whatever fits in RAM.
Is there someone who can explain us scholars how FRP can be noted down formaly? Even a link to a page of a paper that shows the formula would be enormously useful. I'll put all the good papers that I've found in the footnoes.
The following are a few of the good papers on FRP (ordered by subj. quality). I've read the Elm paper, but it's vague despite having a lot of maths in it. What it really decribes is not FRP, but Elm (also it's not exactly FRP).
 Higher-Order Functional Reactive Programming without Spacetime Leaks - https://www.mpi-sws.org/~neelk/simple-frp.pdf
 An Axiomatic Semantics for Functional Reactive Programming - http://www.wpi.edu/Pubs/ETD/Available/etd-042908-133033/unre...
 Push-pull functional reactive programming - http://conal.net/papers/push-pull-frp/
 Categorical Semantics for Functional Reactive Programming with Temporal Recursion and Corecursion - http://www.cs.bham.ac.uk/~pbl/msfp2014/catsemfrp.pdf
 Completeness of Conversion between Reactive Programs for Ultrametric Models - http://www.cs.le.ac.uk/people/fdv1/fdv1/Distribution/SeveriD...
 Fair Reactive Programming - http://www.cs.mcgill.ca/~acave1/papers/fair-reactive.pdf
 A Survey on Reactive Programming - http://soft.vub.ac.be/Publications/2012/vub-soft-tr-12-13.pd...
 Signals, Not Generators! - http://www.ioc.ee/~wolfgang/research/tfp-2009-slides.pdf
The bad thing about all those papers is that a) you need to know Category Theory, b) Temporal Logic, c) Denotational Semantics d) (and optionally Henkin and Ultrametric Models) Unfortunately I'm not yet that smart :( hope someone can bring clarity into FRP
Sorry for the ugly footnote, that's the best I got using HN's formatting capability.
Update: I used unicode spaces, that fixed it.
Will people ever stop using this reasoning? Why not pick the best/better language instead?
Mind that term, actually: while French itself was just an average European language, it was handy, for political reasons, for diplomats of all stripes to pick up—and somewhat easy, presuming you had studied texts in Latin. JS is much the same: a lot of us might not prefer it when writing our own code, but we can all certainly read it, given its ALGOL derivation, and it's certainly more pithy than, say, C, for communicating the kind of concepts we need to communicate (like FRP.)
Mind that term even more, because despite the similar "fr--" sound, it does not actually denote the French language.
"Lingua franca" is actually an Italian term for "Frankish language", during a time (post-middle-ages) when "Franks" often referred to Western Europe in general.
While French is an example of a language which fit the definition for a period of time, it is not the source of its name.
First, most everybody has a JS engine handy, right in the browser of his choice. JS is reasonably standardized - the subset of it which is common between all browsers is pretty big. Then, JS is rather well known, since all web development has to include that - and that's a sizeable part of all development. Next, the language itself isn't that bad - you may write procedural, object-oriented or functional code relatively easy. Even better, the implementations are quite good lately, being forced to evolve under the pressure of demand.
> all these points?
It's straightly viewn from an implementor's perspective in my opinion.
Tl:Dr.: The observer-pattern is bad and encourages callback-hell, therefore a Data-Flow DSL is implemented to control the order of evaluation. It's kind of showing the implementation of an IO-Monad.
Not sure what the alternative would be.Python/C/C++ on linux maybe.
But on Windows? what language does Windows support from scratch that Linux supports?
That and it's very applicable to current problems with web-based programming of the coordination of events, promises, and any other I/O such as Web Sockets, XHRs, etc.
Chaining is the new goto. Abused, it causes deeply anti-modular, temporally coupled code, which is really really ugly to maintain (good thing rockstar ninja programmers only need to think two weeks ahead).