# recent broadcast changes (iterate by default), scalar struct, and \`@.\`

**URL:** <https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178>\
**Category:** Internals & Design\
**Tags:** broadcast\
**Created:** [May 27, 2018, 3:48am UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178 "2018-05-27T03:48:05Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ederag](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ederag/32/4106_2.png) [@ederag](https://discourse.julialang.org/u/ederag)\
**Post date:** [May 27, 2018, 3:48am UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/1 "2018-05-27T03:48:05Z")

</div>

The current broadcasting behavior is non-intuitive when used with `@.`  
Julia Version 0.7.0-DEV.5222,  
Commit 7144b6b816 (2018-05-25 20:23 UTC)

This is a minimal example, stripped down from a real use case.

```julia
struct Material
    a::Float64
end
chi(m::Material, energy) = m.a / energy

m1 = Material(1.0)
energy = 1.5:0.5:2.5
chi1 = @. chi(m1, energy)

┌ Warning: broadcast will default to iterating over its arguments in the future. Wrap arguments of
│ type `x::Material` with `Ref(x)` to ensure they broadcast as "scalar" elements.

```

This is probably following  
[https://github.com/JuliaLang/julia/pull/26435](https://github.com/JuliaLang/julia/pull/26435)

Replacing m1 with Ref(m1) in the last line, as suggested by the error message, results in an error:

```julia
chi1 = @. chi(Ref(m1), energy)
┌ Warning: broadcast will default to iterating over its arguments in the future. Wrap arguments of
│ type `x::Material` with `Ref(x)` to ensure they broadcast as "scalar" elements.
│ caller = ip:0x0
└ @ Core :-1
ERROR: MethodError: no method matching chi(::Base.RefValue{Material}, ::Float64)
Closest candidates are:
  chi(::Material, ::Any) at REPL[2]:1

```

Following the doc and adding  
`Base.broadcastable(m::Material) = Ref(m)`  
does help (no warning, no error).  
But it this is a very surprising behavior:  
by default, a `struct` should be considered as a scalar.

I’ll have to read again relevant issues such as  
[https://github.com/JuliaLang/julia/issues/18379](https://github.com/JuliaLang/julia/issues/18379)  
[https://github.com/JuliaLang/julia/issues/18618](https://github.com/JuliaLang/julia/issues/18618)  
for now, it is not clear why broadcasting could not be an opt-in behavior,  
with iterators opting-in in their interface definition.

Another take, showing that the `Ref()` tip does not work:

```julia
f(x, y) = println(x, " ", y)
x = 1:3
@. f(x, Ref(x))
1 Base.RefValue{Int64}(1)
2 Base.RefValue{Int64}(2)
3 Base.RefValue{Int64}(3)

```

Trying to prepend a `$` to prevent broadcasting (like before a function)  
did not work either.

```julia
@. f(x, $x)
1 1
2 2
3 3

```

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [May 27, 2018, 5:40am UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/2 "2018-05-27T05:40:31Z")

</div>

Because `@.` is a syntax-level transformation, it doesn’t treat `Ref` as a special case in any way. So your

```julia
@. chi(Ref(m1), energy)

```

becomes

```julia
chi.(Ref.(m1), energy)

```

which does indeed try to call `chi(Ref(m1), e)` for each `e` in `energy` and thus produces the error you’re seeing.

If you don’t use `@.`, then the `Ref` works fine:

```julia
julia> chi.(Ref(m1), energy)
3-element Array{Float64,1}:
 0.6666666666666666
 0.5               
 0.4 

```

But I agree that the deprecation warning isn’t very helpful in this case, and you probably won’t be the only person to encounter this issue.

I’m not sure what could be done here. Using a 1-element tuple instead of a Ref also fixes the warning and works fine with `@.`:

```julia
julia> @. chi((m1,), energy)
3-element Array{Float64,1}:
 0.6666666666666666
 0.5               
 0.4 

```

Perhaps we should be recommending that instead?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 27, 2018, 6:00am UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/3 "2018-05-27T06:00:19Z")

</div>

> [@rdeits](#):
>
> Perhaps we should be recommending that instead?

I think that the cleanest solution would be a dedicated wrapper type that just protects from broadcast and does nothing else, along the lines of [#18379](https://github.com/JuliaLang/julia/issues/18379). All other uses can clash with something.

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [May 27, 2018, 6:13am UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/4 "2018-05-27T06:13:20Z")

</div>

But even a dedicated wrapper type would still do the wrong thing in the case of `@.` . If you write:

```julia
@. f(Wrapper(x))

```

then this expands to `f.(Wrapper.(x))` which will try to actually call `f` on a `Wrapper` object, rather than on `x`, just as with the `Ref` above.

Of course, the `@.` macro could be modified to special-case `:Wrapper` calls, but that would break referential transparency, as `const my_wrapper = Wrapper` would cause broadcasting to behave differently.

The tuple trick works here because `(x,)` isn’t a function call and doesn’t have to be transformed by `@.` .

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [May 27, 2018, 6:24am UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/5 "2018-05-27T06:24:54Z")

</div>

Actually, maybe I’m wrong? It might be possible to use the fancy new lazy broadcast to make this work. I’m still learning the new API.

---

<div class="post-metadata">

**Author:** ![greg\_plowman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/greg_plowman/32/8100_2.png) [@greg\_plowman](https://discourse.julialang.org/u/greg_plowman)\
**Post date:** [May 27, 2018, 7:18am UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/6 "2018-05-27T07:18:01Z")

</div>

Would this work?

```julia
@. chi($Ref(m1), energy)

```

Checkout the transformation with `macroexpand`:

```julia
Julia-0.7.0-DEV> macroexpand(Main, quote @. chi(Ref(m1), energy) end)
quote
    #= REPL[21]:1 =#
    chi.(Ref.(m1), energy)
end

Julia-0.7.0-DEV> macroexpand(Main, quote @. chi($Ref(m1), energy) end)
quote
    #= REPL[23]:1 =#
    chi.((Ref)(m1), energy)
end

```

---

<div class="post-metadata">

**Author:** ![ederag](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ederag/32/4106_2.png) [@ederag](https://discourse.julialang.org/u/ederag)\
**Post date:** [May 27, 2018, 3:26pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/7 "2018-05-27T15:26:37Z")

</div>

> [@greg\_plowman](#):
>
> Would this work?
> 
> ```julia
> @. chi($Ref(m1), energy)
> 
> ```

indeed, not pretty, but it works.

---

<div class="post-metadata">

**Author:** ![ederag](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ederag/32/4106_2.png) [@ederag](https://discourse.julialang.org/u/ederag)\
**Post date:** [May 27, 2018, 3:26pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/8 "2018-05-27T15:26:39Z")

</div>

Thanks for the explanations.

```julia
@. chi((m1,), energy)

```

works, but because of the additional coma, seems less readable than

```julia
@. chi([m1], energy)

```

Admittedly the `@.` macro could intercept these single element array construction and transform them to `Ref()`.

But it is still surprising that a simple `struct` type is not treated as simply as possible by broadcasting.  
That is, as a scalar.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 27, 2018, 4:12pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/9 "2018-05-27T16:12:00Z")

</div>

Thinking about this, overly eager broadcasting is only a problem with `@.`, which is a _syntactic_ transformation, so escaping it should be achieved with syntax, too; and thus using `$` is probably the best solution.

> [@ederag](#):
>
> But it is still surprising that a simple `struct` type is not treated as simply as possible by broadcasting.  
> That is, as a scalar.

Note that above the problem comes not from broadcasting _per se_, but the `@.` macro, for which all `f(...)` forms are function calls, and so it can’t distinguish those functions which happen to be constructors for a type.

---

<div class="post-metadata">

**Author:** ![ederag](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ederag/32/4106_2.png) [@ederag](https://discourse.julialang.org/u/ederag)\
**Post date:** [May 27, 2018, 5:03pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/10 "2018-05-27T17:03:57Z")

</div>

With basic broadcasting the recommended `Ref()` does work, which is not the case in a `@.` macro, true.

But there is a warning too:

```julia
chi.(m1, energy)
┌ Warning: broadcast will default to iterating over its arguments in the future. Wrap arguments of
│ type `x::Material` with `Ref(x)` to ensure they broadcast as "scalar" elements.

```

Having to use `Ref()` here is non-intuitive.  
Should basic types be treated as scalars, `Ref()` would not be needed at all.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 27, 2018, 5:18pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/11 "2018-05-27T17:18:58Z")

</div>

> [@ederag](#):
>
> Should basic types be treated as scalars, `Ref()` would not be needed at all.

Perhaps my point was not clearly communicated: there are no “types” in the expression `@.` gets, only function calls in an abstract syntax tree. Even type constructors are just function calls, and for a `f(args...)` expression, `@.` can’t know of `f` is an (inner) constructor, for multiple reasons.

Consider:

```julia
ex1 = :(@. Foo(x, y))

ex2 = macroexpand(ex1)

```

Then both

```julia
struct Foo
    x
    y
end

eval(:(let x = $([1, 2]),
           y = $([3, 5])
       $ex2
       end))

```

and

```julia
Foo(x, y) = x+ y

eval(:(let x = $([1, 2]),
           y = $([3, 5])
       $ex2
       end))

```

are valid.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [May 28, 2018, 2:51pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/12 "2018-05-28T14:51:29Z")

</div>

Thanks for bringing this up — it’s a very good point that the suggested deprecation simply won’t work in a `@.` deprecation. There are several alternatives, but they all have their downsides:

- You can protect the `Ref` from being dotted by using `$Ref` like was suggested above. This is a special syntax that just applies to the macro, though, so it’s not terribly intuitive.
- You can also use a tuple literal like `@. f(x, (y,))` to treat `y` kind-of like a scalar. For most purposes this should be just as fast and scalar-like as a `Ref`, but it’s not 100% scalar-like: if `x` is scalar-like or a zero-dimensional array, `f.(x, Ref(y))` will return an unwrapped scalar, whereas `f.(x, (y,))` will return a 1-tuple. Some array types might also not support broadcasting with tuples as nicely as they do scalars.

The solution here, I think, will be a special syntax that will work everywhere to put values in a `Ref`. We’ve been talking about `&y`. That’s currently available and would make for a better alternative all around.

---

<div class="post-metadata">

**Author:** ![jebej](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jebej/32/1784_2.png) [@jebej](https://discourse.julialang.org/u/jebej)\
**Post date:** [May 28, 2018, 4:31pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/13 "2018-05-28T16:31:23Z")

</div>

Is it that advantageous to not treat `struct`s as scalars? It seems like this adds a lot of complexity for what appears to me would be the most popular use-case…

---

<div class="post-metadata">

**Author:** ![jeff.bezanson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeff.bezanson/32/48_2.png) [@jeff.bezanson](https://discourse.julialang.org/u/jeff.bezanson)\
**Post date:** [May 28, 2018, 4:33pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/14 "2018-05-28T16:33:14Z")

</div>

I advocated pretty strongly for the new broadcast behavior, so I can address that part. The first problem is that it’s useless for `f.(x)` to be equivalent to `f(x)` — you bothered to write a dot, so we ought to do something with it.

We could potentially base broadcasting on whether an argument implements `iterate`, but that makes the calling code kind of opaque: given `f.(a, b, c, d)` you have to “guess” which arguments are iterable. It’s also conceivable that a type could add or remove an `iterate` method, and that would silently change the behavior of your program. So we’re erring on the side of explicitness here.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [May 28, 2018, 4:47pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/15 "2018-05-28T16:47:14Z")

</div>

There’s also a number of asymmetries here:

- In the future, we’ll iterate over the object or throw an error. You can always treat objects as scalars, but you cannot always treat them as iterables. By defaulting to errors for non-iterables, it’s a great big signal for library creators that they can opt-in to acting like a scalar if it is appropriate.
- End-users can opt-in to scalar-like behaviors by wrapping in another broadcastable container (like `Ref` or 1-tuples or 0-dimensional arrays). Were we to default to treating everything like a scalar, that means that they would have to collect an iterable into a different broadcastable structure in order to iterate over it.
- In 0.6 and prior, we had it the other way around —defaulting to scalar-like behavior it meant that it was up to each library and type to decide how they’d behave. Even with base itself, this led to lots of surprises — some folks wanted to iterate over sets but treat dicts as scalars. Or broadcasting the array-ish Linear Algebra `I` object was quite a surprise.

There’s going to be some pain in converting to this new system, but I think the resulting consistency will make it much easier to understand what a given broadcasting expression will do.

---

<div class="post-metadata">

**Author:** ![jebej](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jebej/32/1784_2.png) [@jebej](https://discourse.julialang.org/u/jebej)\
**Post date:** [May 28, 2018, 4:58pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/16 "2018-05-28T16:58:45Z")

</div>

Does that mean that in the future, something like `rand(3,3) .+ 3` would throw? I guess not, meaning that numbers will be treated specially.

Also, since any type can be treated as a scalar with `Base.broadcastable(m::T) = Ref(m)`, knowing what an expression will do is not more transparent.

> [@mbauman](#):
>
> In the future, we’ll iterate over the object or throw an error.

Couldn’t we ~~broadcast~~ iterate over iterables and instead of throwing an error, just treat non-iterables as scalars?

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [May 28, 2018, 5:12pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/17 "2018-05-28T17:12:10Z")

</div>

The point is that we strongly advocate _not_ implementing `broadcastable(m::T) = Ref(m)` if `m` is iterable. Sure, custom types can break this norm (and, in fact, strings do), but we want the exceptions to this rule to be rare and, well, exceptional. Numbers are actually iterable and 0-dimensional-like structures, so they’re not a special case. Types are a good example of deciding to opt-in to scalar-like behaviors (for things like `round.(Int, A)`), but they’re not iterable, so they don’t violate the norm.

If we were to default to scalars, then we end up with a mishmash of behaviors because some iterables will — intentionally or not — use that default while others will implement the fancier behavior.

Now, you’re right, we could try to do what you mean in the fallback, based upon the existence of the `iterate` method. That’s not something we’ve really done in any base API yet, and as far as I know, it’s not really optimized to be used as such yet. By being conservative here, we can turn this error into the scalar functionality if we implement the fast optimization and decide we want this in the future.

---

<div class="post-metadata">

**Author:** ![jebej](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jebej/32/1784_2.png) [@jebej](https://discourse.julialang.org/u/jebej)\
**Post date:** [May 28, 2018, 5:37pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/18 "2018-05-28T17:37:14Z")

</div>

> [@mbauman](#):
>
> The point is that we strongly advocate _not_ implementing `broadcastable(m::T) = Ref(m)` if `m` is iterable.

I agree. I am trying to see is if we can avoid having to do that if `m` is _not_ iterable.

> [@mbauman](#):
>
> Numbers are actually iterable

Hmm that seems strange to me, shouldn’t numbers not be iterable? What does it mean to iterate over a single number?

> [@mbauman](#):
>
> If we were to default to scalars, then we end up with a mishmash of behaviors because some iterables will — intentionally or not — use that default while others will implement the fancier behavior.

Hmmm, I don’t see why. The default would be to iterate if the object is iterable, and treat as a scalar otherwise.

If someone makes a particular object that is both iterable and behaves as a scalar under broadcast (by using the above `broadcastable` definition), that definitely would be confusing, but that situation is not changed by my proposal.

> [@mbauman](#):
>
> Now, you’re right, we could try to do what you mean in the fallback, based upon the existence of the `iterate` method. That’s not something we’ve really done in any base API yet, and as far as I know, it’s not really optimized to be used as such yet. By being conservative here, we can turn this error into the scalar functionality if we implement the fast optimization and decide we want this in the future.

How do we currently decide if an object will be iterated under broadcasting?

To make sure I understand properly, this is my summary:

it seems like the current design is

```julia
if x is iterable; iterate under broadcast
elseif x is a Ref (or some kind of 0D object); treat x as a scalar under broadcast
else throw error

```

which I am suggesting be replaced with the simpler

```julia
if x is iterable; iterate under broadcast
else; treat x as a scalar under broadcast

```

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [May 28, 2018, 5:54pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/19 "2018-05-28T17:54:54Z")

</div>

There are a number of different designs bouncing around in this thread:

- The 0.6 behavior of defaulting to scalar — this is what I was addressing in “If we were to default to scalars…”
- The 0.7 behavior with depwarns — this behavior is the worst of all worlds and a transition to the:
- The 1.0 behavior of defaulting to iteration — this will error if the struct isn’t iterable
- Your proposed test for iterability and either iterate or treat as scalar

Broadcasting requires `axes`, indexing, and `ndims`. We call `broadcastable` on all arguments to allow them to return something that supports those methods.

In 1.0, the default `broadcastable` method will be:

```julia
broadcastable(x) = collect(x)

```

Custom types can add their own optimizations to avoid allocating the intermediate structure — for example, arrays support those three methods so we define `broadcastable(A::AbstractArray) = A` for them.

---

<div class="post-metadata">

**Author:** ![jebej](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jebej/32/1784_2.png) [@jebej](https://discourse.julialang.org/u/jebej)\
**Post date:** [May 28, 2018, 6:24pm UTC](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178/20 "2018-05-28T18:24:53Z")

</div>

Ah I see, currently there is no test for iterability: we just call the method and if it doesn’t exist it errors out. I thought there might have been a trait for that.

Yes in that case my suggestion is harder to implement.

[Next page](https://discourse.julialang.org/t/recent-broadcast-changes-iterate-by-default-scalar-struct-and/11178.md?page=2)
