You've identified hot and cold observables.
Observable.range
returns a cold observable, though you're describing the resulting queries in a hierarchy as if they're hot; i.e., as if they'd share subscription side effects. They do not. Each time that you subscribe to a cold observable it may cause side effects. In your case, each time that you subscribe to range
(or to queries established on range
) it generates a range of values.
In the second point of your research, you've identified how to convert a cold observable into a hot observable; namely, using Subjects. (Though in .NET you don't use a Subject<T>
directly; instead, you'd use an operator like Publish. I suspect RxJava has a similar operator and I'd recommend using it.)
Additional Details
The definition of hot by my interpretation, as described in detail in my blog post linked above, is when an observable doesn't cause any subscription side effects. (Note that a hot observable may multicast connection side effects when converting from cold to hot, but temperature only refers to the propensity of an observable to cause subscription side effects because that's all we really care about when talking about an observable's temperature in practice.)
The map
operator (Select
in .NET, mentioned in the conclusion of my blog post) returns an observable that inherits the temperature of its source, so in your bottom diagram c
, a
and b
are cold because d
is cold. If, hypothetically, you were to apply publish
to d
, then c
, a
and b
would inherit the hot temperature from the published observable, meaning that subscribing to them wouldn't cause any subscription side effects. Thus publishing d
converts a cold observable, namely range
, into a hot observable.
.--> c --> a
d --|
.--> c --> b
However, your question was about how to share the computation of c
as well as d
. Even if you were to publish d
, c
would still be recomputed for both a
and b
for each notification from d
. Instead, you want to share the results of c
among a
and b
. I call an observable in which you want to share its computation side effects, "active". (I borrowed the term from the passive & active terminology used in neuroscience to describe electrochemical currents in neurons.)
In your top diagram, you're considering c
to be active because it causes significant computation side effects, by your own interpretation. Note that c
is active regardless of the temperature of d
. To share the computation side effects of an active observable, perhaps surprisingly, you must use publish
just like for a cold observable. This is because technically active computations are side effects in the same sense as cold observables, while passive computations have no side effects, just like hot observables. I've restricted the terms hot and cold to only refer to the initial computation side effects, which I call subscription side effects, because that's how people generally use them. I've introduced new terms, active and passive, to refer to computation side effects separately from subscription side effects.
The result is that these terms in practice just blend together intuitively. If you want to share the computation side effects of c
, then simply publish
it instead of d
. By doing so, a
and b
implicitly become hot because map
inherits subscription side effects, as stated previously. Therefore, you're effectively making the right side of the observable hot by publishing either d
or c
, but publishing c
also shares its computation side effects.
If you publish c
instead of d
, then d
remains cold, but it doesn't matter since c
hides d
from a
and b
. So by publishing c
you're effectively publishing d
as well. Therefore, applying publish
anywhere within your observable makes the right side of the observable effectively hot. It doesn't matter where you introduce publish
or how many observers or pipelines you're creating on the right side of the observable. However, choosing to publish c
instead of d
also shares the computation side effects of c
, which technically completes the answer to your question. Q.E.D.