
Functional Reactive Programming in Java - astigsen
https://realm.io/news/droidcon-gomez-functional-reactive-programming/
======
hacker_9
Sounds 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
Once 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
Agreed 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).

------
lemevi

       aObservable.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.

~~~
hepta
Both 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
I 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
Stream 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-...](https://donsbot.wordpress.com/2008/06/04/haskell-as-fast-as-c-working-
at-a-high-altitude-for-low-level-performance/)

------
tunesmith
Lost 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
Extremely hard to implement, or an oxymoron by definition? Doesn't "reactive"
explicitly indicate side effects (aka "reactions")?

------
eurekin
Ah, 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.

------
casidiablo
How's this new at all?

~~~
V-2
For 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.

