First of all, I have a hard time even to visualise the corresponding computational graph. Below is my attempt to indicate by the dashed arrow that the computation of sum(x) is triggered by changes of x defined upstream, but not by changes in n:
Put in words, the middle node (the 3d cell) doesn’t trigger downstream computations: sum(x) (the 4th cell) is not recomputed whenever n changes its value.
This ill-defined behaviour is just a symptom. It is easy, of course, to link n to sum(x) to obtain a well-defined state in this case. My objective, however, is to understand how could I introduce an explicit edge between nodes/cells 3 and 4, without referencing upstream n.
What triggering technique could be used to this end?
I know this is probably not what you are looking for, but my answer would be to combine expressions into cells using begin ... end (like 1 and 3) or to introduce a function that returns a modified copy of x instead of the in-place modification that you have now.
The core issue here is that modification have no deeper order. E.g.
Consider cells like:
# cell 1
x = Ref(2)
# cell 2
x[] = 5
# cell 3
@show x
Then it is clear that cell 1 must be executed first becuase it defines the variable x used in the other cells. But there is no unambigous order of cells 2 and 3.
You can introduce “auxiliary variables” to establish an order like:
# cell 1
x = Ref(2)
# cell 2
x[] = 5; aux=nothing;
# cell 3
aux; @show x
Correct! The issue is clear and there is no way to remove the unambiguity automatically. Hence the need to introduce an order explicitly.
Auxiliary variables solution is close. Yet, the goal here is to trigger the follow-up computations only if the input modification actually did take place. I.e., in the given example, I seek to recompute sum(x) only when x[1:2:n] .= 42 was actually executed.
I see now, that it was not clear from the initial description. Sorry.
The if block was meant to illustrate the problem: an aux variable put within the scope of if would result in the aux not defined error for even values of n.
Below, the closest I’ve got to to the required functionality so far.
The last bit is somewhat weird but it does the job: sum(x) is recomputed when either the whole vector x is re-assigned or a subset of its entries gets modified.
Thanks everyone for discussion! Please do let me know if you find of a better way to achieve the desired goal.
# ╔═╡ b6de62ec-afa8-11ee-3cab-c1aacaec6cb3
x = collect(1:10)
# ╔═╡ 10e03418-8a12-4cd9-9283-bf5bcad49618
n = 3
# ╔═╡ aa375da5-dba0-4a0e-8e96-3b0d80d32458
isodd(n) && (aux=nothing; x[1:2:n] .= 42)
# ╔═╡ 669e5597-2506-409d-b13c-39c81539e60a
if @isdefined aux
sum(x)
else
sum(x)
end
The part of Pluto analyzing expression and determining their order and graph has been recently moved to separate packages that are standalone registered in General:
You might have a look there for more details or directly use them for your use case if relevant!
Fun question! The “official” answer to the question in the title is: create variables! Pluto only tracks assignments and references, so that’s the mechanism you need to use to tell Pluto about extra edges.
x = collect(1:10)
n = 3
begin
modification_done = nothing
if isodd(n)
x[1:2:n] .= 37
end
end
begin
# reference this variable to create the reactive edge
modification_done
sum(x)
end
But I think that a solution that uses copylike contradict suggested is probably better, as this eliminates any surprises when working on one of the intermediate cells.