Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I've struggled to grok FRP in the past - I hear lots of smart people trumpeting it, but I haven't been able to wrap my head around it. This tutorial was great and I was following along right until it got to the part about modeling the suggestions as streams (https://gist.github.com/staltz/868e7e9bc2a7b8c1f754#modellin...).

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)?



You can think of an animation as a stream of frames; this is how video is implemented. It especially makes sense for video games that typically have a game loop that renders the screen at 30 or 60 frames per second.

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.


Hmm, this sounds like immediate-mode GUI that is a well known design in games. Since games people are used to rendering the view anew on each screen refresh, it was logical to do this for widgets as well.

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.


Immediate mode UI is more continuous (refresh every 15 ms) and not stream-based, which is actually quite different from Elm and is more like React.


> my brain doesn't want to think about those UI elements as streams

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 was struggling with the way the operators are written:

  clickStream.map(f).scan(g);
Then I realised that map() and scan() are not doing much when that line of code is executed. They only add a new operation step to a list of operations for clickStream. Only when an event is received, the saved list of operations gets executed. So, I think a better naming would have been addMap() and addScan(), because they only add operators. They don't map/scan anything when they are called.

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. :)


Streams are lazy. They work in the same manner as lazy collections in languages such as Scala and Haskell - if you have an infinite collection, say, the set of all prime numbers, and do a map operation on it, it does not try to eagerly execute an infinite loop. Instead, the mapping is only done when when you actually request any of the elements of the set.


> So, I think a better naming would have been...

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.


When working with functional programming, thinking about "doing" is often the wrong approach, because "doing" is often tied to "change of state". Instead, think about translations/transformations from "what I have" to "what I want".

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.


You can try our Learn RxJS tutorial here as well: http://reactive-extensions.github.io/learnrx/


"add___" might imply mutation of the stream, which it seems to me (just reading this tut for the first time) isn't the mindset the framework wants you to be in.


Why not try watching the video from Jafar Husain, a tech lead from Netflix, talk about Async JavaScript at Netflix which uses RxJS to explain these concepts? https://www.youtube.com/watch?v=XRYN2xt11Ek


+1, I've seen this talk, very approachable.


I came to FRP in a functionall language (Haskell) so I am not sure how applicable my experience is.

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.


Hi, can you make that question on the gist so that the discussion persists there? I'd be glad to try to fill in the missing mental bits there.


Sure thing :)


Many claim that reactive approach somehow makes thing easier, to me it just looks like a solution (not really) in search of the problem. So for now I just dismissed it as a fad. Will see.


It's not a fad at all, with Netflix and other companies using Rx in all flavors on their platforms. See this video from Jafar Husain, a tech lead at Netflix, talk about how they use RxJS at Netflix: https://www.youtube.com/watch?v=XRYN2xt11Ek


It is possible that I am wrong, but I Netflix using it does not necessarily proves that it is not a fad.


Ok, the consider GitHub for Mac and Windows are both written using Rx frameworks. In addition, so is Cortana from Microsoft and has been in the phone ROM since day 1


FWIW I don't think FRP is a fad, but big companies have lots of projects going on all over the place and it isn't uncommon for an engineering team to be given permission to trail some tech, especially at an OSS shop. Netflix might have some team(s) using some Brand New Library but that isn't sufficient evidence for that tech not being a fad. Switching costs are high at companies that remain in business and are growing so it's not uncommon for a big shop to have some random tech written in fad code that people absolutely despise in the future.


If Netflix uses it, it must not be a fad! Silverlight is poised to take over the WORLD!!!




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: