Understanding the behaviour of @. macro

Yeah, this is a very confusing one, verging on cursed. One way to think of it is that broadcast is creating a simple loop over all the dotted expressions, so you can think of rand.(xs) .+ ys as something kinda like [rand(x) + y for x in xs, y in ys]. But that won’t work because rand(10) gives you a vector and you can’t add (without broadcasting, but remember we already did the broadcast transform) a scalar to a vector.

There are a few things that make this especially confusing:

  • 5 .* rand.(10) works, because you can multiply a scalar by a vector.
  • rand.(10)by itself — is functionally equivalent to rand(10) because, at the end of a broadcast expression, Julia “unwraps” zero-dimensional results. Theoretically, the “correct” answer to rand.(10) would be a zero-dimensional array that contains a 10-element vector, but that’s quite cumbersome in many situations. See julia#28866 for more on this.

The simplest guideline is that broadcasting works most seamlessly for functions functions that take scalars and return a single value. To avoid @. from affecting functions like rand (or ones or zeros or any of the like), you can “protect” them with a $. That is, @. 5 * $rand(10) + 2.

16 Likes