Makie and observables

Disclaimer : I’m totally unsure if this question is sound or not.

In the Makie - Problems with synchronous updates documentation, it is clearly stated that multiple updates of the plots can occur when only one really is needed, and that this situation can may be error-prone.

The strategy involving calling x.val = ... instead of x[] = ... allows to avoid this kind of situation, but first, these have to be properly detected.

So I was wondering, is there an official or at least recommended way to detect such unwanted updates ? A real like example could be

  • a custom recipe myplot
  • myplot has an attribute prop1
  • prop1 is used to obtain an observable o1
  • both prop1 and o1 are lifted to obtain another observable o2
    So far, if I understood correctly (but maybe I’m totally wrong), this should result in two updates
  • o1 and o2 update due to prop1 update
  • o2 update due to o1 update

If I interpreted the Observable behavior correctly (which I’m totally unsure of), I think it would be practical to have something, probably a macro, for development purposes, that would result in the following behavior.

f,ax,p = @track_observable_updates myplot(...)
p.prop1 = 0

Updated observables :
- prop1 -> 0
    |- o1 -> v1
    |- o2 -> v21
- o1 -> v1
    |- o2 -> v22

Is this feasible, or at least useful compared to currently available tools ?
I’m in the dark here. I’m guessing @jules @sdanisch or @ffreyer are the best suited to clarify that.

One problem is that an observable doesn’t directly store all the necessary information to compute that graph. Consider:

o1 = Observable(1)
o2 = Observable(2)

on(o1) do val
    o2[] = val
end

Now you’d have to somehow analyze this closure’s code to detect that a triggering of o2 is in there which would make the graph o1 -> o2. And I don’t think that’s easily possible.

I’m very unfamiliar with macros, but isn’t it precisely their typical use case ? I mean, to be able to modify the code of their argument ?

Macros would see an empty setindex! but they wouldn’t know what the argument types are.

You are right indeed ! I’m wondering if retrieving all ObserverFunctions declared in a scope could help.

It should be possible to write a macro that converts something like

@track_updates on(o1) do val
    o2[] = val
end

to

track_updates!(o1, o2) # register o2 as dependent of o1
on(o1) do val
    o2[] = val
end

The only difficult part is to scan the Expr for [] calls in the do body, but this is doable using MacroTools.jl.
And the track_updates! method should specialize to only track Observables and ignore things like Refs.

However, this becomes tricky (or inaccurate) quite easily

@track_updates on(o1) do val
    if rand(Bool)
       o2[] = val
    else
       o3[] = val
    end
end