Observables versus Reactive

Checkout Observables.jl, looks cool and super useful.

I don’t think I really understand the difference between and Observable and a Reactive.Signal. Anyone here can explain? When would one be better than the other? Why? How? What can one do that the other doesn’t? Why is that special?

I like new and shiny things.

Looks very cool, I quite like the packages that Shashi has worked on previously.

Quoting from the documentation:

How is it different from Reactive.jl?

The main difference is Signals are manipulated mostly by converting one signal to another. For example, with signals, you can construct a changing UI by creating a Signal of UI objects and rendering them as the signal changes. On the other hand, you can use an Observable both as an input and an output. You can arbitrarily attach outputs to inputs allowing structuring code in a signals-and-slots kind of pattern.

Another difference is Observables are synchronous, Signals are asynchronous. Observables may be better suited for an imperative style of programming.

Yea, I read that, but I still don’t get it… Like, what do they mean with “Observable both as an input and an output”, an input to what?

I interpret that as follows:

using Observables

const input = Observable(1)
const output = Observable(0)

on(input) do x
    output[] = 2*x
end

on(output) do x
    println("output set to $x")
end

input[] = 1
input[] = 2
input[] = 10

This prints:

output set to 2
output set to 4
output set to 20

Fine, but I don’t understand how that’s much better/different than:

using Reactive

const input = Signal(1)
const output = map(input) do x
    2x
end

foreach(output) do x
    println("output set to $x")
end

push!(input, 1)
push!(input, 2)
push!(input, 10)

Which also prints:

output set to 2
output set to 4
output set to 20

Like, what’s the novel feature here?

I think the difference is quite subtle, in the case of Reactive to get the output you actually need to create a new Signal (this is hidden by the foreach), while in Observables any Observable can call any number of functions without creating a new Observable.

To expand a bit on your question, for me the main difference is simplicity: every Observable basically stores a list of functions to call upon change, and these are just called synchronously without intervention of the task system.

Interesting. I’m writing a semi-large GUI right now. I’m making heavy use of Reactive and GtkReactive, and I therefore have tons of signals everywhere. I’m curious about how it would look and work with these observables.

Thanks for the feedback @barche, but I’m still unclear on the main advantages Observables have over Signals… Are we talking better as-in observables are going to replace signals in the future, or just different use-cases? And if so, what would differ between the two use-cases?

There’s also ReactiveBasics which acts synchronously but has the API of Reactive.jl.

https://github.com/tshort/ReactiveBasics.jl

2 Likes

Wow! Cool stuff! Irritating that I missed it.

The use-cases are different, in my opinion. Reactive is asynchronous, which might require a yield to force an update, as the following example illustrates:

using Reactive

the_value = 0

s = Signal(0)

s2 = map(s) do x
    global the_value
    # try with println, it interferes with the event queue and breaks this demo
    ccall(:jl_safe_printf,Void,(Ptr{UInt8}, Vararg{Int32}), "setting value to $x\n")
    the_value = x
end

push!(s, 1)

print("value after push is $the_value\n")
yield()
print("value after yield is $the_value\n")

So if you want the_value to change as soon as signal s is updated, the synchronous approach is a better fit, but if you want the_value to update in the background then an asynchronous system like Reactive is needed.

That sounds like that is the difference between Reactive and ReactiveBasics.jl. I’m sorry to insist, but I’m fishing for more than the asynchronous versus synchronous difference… Or maybe that’s just it?

As far as I can tell, the difference is conceptual: in the Reactive framework, signals are only connected to other signals, and input is triggered by pushing to the “source” signal. In your example, the foreach hides the fact that to do anything when output changes you need to create a new signal. So signals are aware of each other and connected to each other by mapping functions that are intended to transform the input signal into the output signal, thus forming a sort of processing pipeline.

Observables on the other hand are just values that can call functions when their value changes. There is no inherent network between observables, unless one of the observers (= just a Julia function) decides to update another observable.

Personally I would like to take the way Observables one step further: a changing value is just one possible event, so separating the event notification system from objects holding a value should be possible. This would allow for events that don’t have a value, such as notifying that a button was clicked, or events that have more than one value associated with them. A very basic prototype of how this might work is here:

https://github.com/JuliaGizmos/Observables.jl/pull/6

CC @tim.holy and @shashi in case they missed this thread and want to “react” :wink:

3 Likes