Very interesting readings.
So, let’s see if I got it right this time.
Meta.@lower I see that
ones.(2,2).*rand.(2,2) is basically being “translated” into
julia> using .Broadcast: materialize, broadcasted
julia> bc = broadcasted(*, broadcasted(ones, 2, 2), broadcasted(rand, 2, 2))
Base.Broadcast.Broadcasted(*, (Base.Broadcast.Broadcasted(ones, (2, 2)), Base.Broadcast.Broadcasted(rand, (2, 2))))
bc holds everything necessary to “unfold” the computation, which if I understand correctly is then only performed by the
Somewhat differently instead, if I call
ones(2,2).*rand(2,2) this translates into
julia> bc = broadcasted(*, ones(2,2), rand(2,2))
Base.Broadcast.Broadcasted(*, ([1.0 1.0; 1.0 1.0], [0.19239965952523885 0.8796361629139042; 0.1939935696169941 0.0831963706586214]))
which I could also achieve by writing
a, b = broadcasted(ones, 2, 2), broadcasted(rand, 2, 2)
bc = broadcasted(*, materialize(a), materialize(b))
So, I went and checked how
materialize works. I’m not entirely sure I understand it but, from what I got, it
instantiates the (otherwise lazy)
So the difference between calling it only only on the “fused” expression and calling it in each single case is:
@. ones(2,2)*rand(2,2) does not “apply” (
instantiate) any function/object until the very end
broadcasted(*, ones(2,2), rand(2,2)) instead “
instantiates” the two arrays before calling the broadcast operation (though
instantiate is probably not the correct term here).
I think I got it. I still find it unintuitive from a “high-level” perspective, but at least I understand a bit what’s going on now (which will hopefully prevent bugs in the future).