5 ms·
Functional Reactive Programming in Java
- tunesmith 11y agoLost in all the complaining about FRP's etymology is that the real FRP meets a basic principle that Rx doesn't - it's purely functional. Deterministic, referential transparency, etc. That's why it's useful, and that's also why there aren't many libraries for it, it's apparently extremely hard to implement. I really like Rx and I am using it in projects I'm currently working on, but there's still that basic discomfort that it's not purely functional.
- V-2 11y agoExtremely hard to implement, or an oxymoron by definition? Doesn't "reactive" explicitly indicate side effects (aka "reactions")?
- lemevi 11y agoaObservable.filter(n -> n % 2 == 0) .map(n -> n * n) .subscribe(System.out::println); I love functional programming, but there's something that a FP programming language Haskell brings to it that you don't wont get in attempts to bring FP to other languages, and that's lazy evaluation. For example, when using FP in other languages I worry about how the functions I stack together to evaluate lists are implemented. I sincerely doubt that they have Haskell's optimizations and are actually continuously and unnecessarily iterating over everything in each function call multiple times. Haskell doesn't actually do this even though that's how the code might read. So while I love declarative programming and writing and using functions that don't have side-effects I'm not sure that unless it's supported by the actual engine powering the language that it's the best idea. In fact it may be a bit cargo-cult programming, it looks like FP, but it doesn't actually work like FP. Additionally, regarding the observer pattern in my experience that leads to tightly coupled code where you can't always easily trace the line of execution or separate it out later when you need to. It's like global streams of communication, sort of like using global variables to pass state around. I can't tell from the article if they've addressed that.
- cwyers 11y agoThat's more of a Haskell thing than an overall functional language thing, isn't it? OCaml uses strict evaluation.
- bbatha 11y agoThough OCaml supports lazy very well with the lazy keyword.
- davexunit 11y agoFunctional programming is orthogonal to lazy or eager evaluation.
- hepta 11y agoBoth Clojure and Scala work on the JVM, both have lazy sequences, Scala even has lazy evaluation. It's not the same as fusion, but it's not unnecessary iteration either. These are not invalidated by being on the JVM, Java can do some of these as well.
- jerven 11y agoI know that the Graal JIT guys are working on chained function call fusing for inlining when generating machine code etc... But I am not sure that is what you call "fusion". There is definitely only one iteration over the collection in the example style code.
- hepta 11y agoStream fusion (in Haskell) is the removal of intermediate lists created when you chain multiple list functions (map, filter, fold, etc). https://donsbot.wordpress.com/2008/06/04/haskell-as-fast-as-c-working-at-a-high-altitude-for-low-level-performance/ https://donsbot.wordpress.com/2008/06/04/haskell-as-fast-as-...
- edgyswingset 11y ago> For example, when using FP in other languages I worry about how the functions I stack together to evaluate lists are implemented. I'm only speaking for languages I know: - C# LINQ API via IEnumerable<T> is lazy (aside from a few like Min/Max) - F# is lazy for any Seq functions or the Lazy() computation - Clojure is lazy for Sequence functions You can verify all three by looking at the source on Github if you'd like.
- casidiablo 11y agoHow's this new at all?
- V-2 11y agoFor an average Java dev (the silent majority, not the ones writing blogs, speaking at conferences etc.) it is new, exotic and mind-boggling I assure you.
- aikah 11y ago> How's this new at all? Nobody said it was new. Do you really think all developers know everything , every paradigms , techniques, frameworks in existence ? no . Do you like FRP ? if yes then why would you complain about FRP vulgarization ? about yet an FRP blog ? Some things might seem obvious to you. Well there are not obvious for everybody because they have other things to worry about in their respective domain. Don't be arrogant. If you like FRP then you want more people to embrace this paradigm. Something will always be new to somebody.
- hacker_9 11y agoSounds so impressive, but as a game dev, how do you overcome these problems: 1. Horrible performance. Instead of for-loops you are wrapping code in ienumerables, yield returns, linq, and event handlers. This is not acceptable for game dev because of the constant freeze frames from the collection of all the garbage generated. But how is this good for web dev either? You are either on the client using javascript which is already 200x slower than native, or on the backend where you could be dealing with 10s of 1000s requests at any moment so saving memory/performance is a priority. 2. 'Subscribe' methods makes behavior hard to pin down. Whenever i see an event in the code, i now have to assume there could be a subscription at some point at runtime. All I have for this is Ctrl+F search my codebase. Or write a boatload of tests to make sure someone doesn't subscribe something somewhere wrongly. This creates a debugging nightmare. The more generic/dynamic code the more you can't see what's going on. Irony of an 'observable' is I can't observe it until I'm running my code which is too late. 3. Streams of data: to be fair, this is a nice feature. I've used it myself to flatten a tree and read as a list for example, where the reader would yield return a struct item { type=node|indent|dedent, node }. It's also nice to read and modify code like this, and quicker to refactor. But it is still wayy slower - your basically wrapping 'if statements' into 'filter(x => ...)' linq objects. The compiler can't optimise this stuff for branch prediction, not to mention the intermediate lists that are created, so for the readability you are losing soo much performance it's just not worth it. If there was a language that could compile down these streams into loops/ifs etc that could be a real win though.
- pjmlp 11y agoOnce upon a time I used to argue about using jump vs call/ret due to execution cycles. Or adding nop opcodes to improve instruction fetching. A few years later I used to argue about direct function calls vs VMT lookups. Once upon a time I used to DW for the whole block I was using. Then came NEW/DISPOSE. Nowadays I only care when the profiler tells me todo so. I imagine you see where I am getting to.
- RyanZAG 11y agoAgreed in general, but it's also the level of inefficiency that matters. In the case of jump vs call/ret and those others, you're looking at a less-than-double increase in time. It's there and you can optimize it, but if you don't you don't really notice. FRP can be very different. With a large chunk of data and the wrong methods, you could be copying and re-copying that data over and over instead of editing it in place. This isn't just a 100% slowdown - you can go from 5 seconds to 500 seconds when converting an in-place data modification to FRP. Basically O(n) vs O(2n) ? Optimize it later if necessary O(n) vs O(n^3) - Uh oh! That's not to say that FRP is bad or you shouldn't use it, but never use a 'magic' mapping in FRP unless you know exactly what it is doing to the data and if it's going to copy or not (hint: it usually is).
- eurekin 11y agoAh, the typical presentation of what it is and not why it's useful. It's just that kind of library that solves a lot of problems, which people don't even know of (or do not want to). I have very little experience with Rx, but still I found some really useful applications for it: 1. Jumping between threads back and forth. This is a typical requirement of a UI Toolkit (Java Swing, Android). I don't remember the exact details, but some special combinators (subscribeOnEdt) were provided out of the box by the library (was using rxScala then). You just connected button to some action, which result was displayed in a text field. With rxScala for Swing it was an extra function call. In plain JDK it's an extreme pain (SwingWorker, SwingUtilities.invokeLater and custom Runnables or threads etc.) 2. Edge case handling in abstractions. Let's say you want to implement a real time moving average of exactly 3 points, but discarding 10 first results. In Rx it is something of a "Hello World" level of complexity. The same thing, in plain Java, which also handles the edge cases correctly is almost always implemented buggy. 3. Event bookkeeping. If you happen to use push model a lot, a funny thing occurs. In case of UI applications some people call it a "glitch". In electronic engineering this is a race condition (https://en.wikipedia.org/wiki/Race_condition https://en.wikipedia.org/wiki/Race_condition). It's a very fundamental problem, which is solved in rx implementation. The merge operator takes care, that the events fired are traversing the dependency graph in a topological order. That way, it is way harder to create that bug (not impossible though). 4. Error handling by default. This is also very very useful. A typical programmer in Java implements handling for all happy cases, few error conditions and most often than not, misses a lot of other error cases. "Who would notice? Besides, exception handling in Java is really ugly." So yeah, they don't write it. With RX the error condition handling is always present, by the means of termination message. Every combinator has a clearly defined semantics for handling those onComplete events. It's just a matter of reifing exceptions and defining stream for error events. You could do it in Java of course, but the amount of boilerplate which is killed by handling this in rx is very impressive.