Neat way of broadcasting getindex and getfield

I often find myself wanting to broadcast getindex and getfield, which is perfectly doable, but could enjoy a nicer syntax. I figured out if I define the operator .. in the right way, I get a kind-of nice syntax for doing this without macros:

a = [randn(3) for _ in 1:4];
b = [complex(i,i+1) for i in 1:4];
(..)(x::AbstractArray,i...) = getindex.(x,i...)
(..)(x::AbstractArray,i::Symbol) = getfield.(x,i)

julia> a..2 == getindex.(a, 2)
true

julia> b..:re == getfield.(b, :re)
true

Edit: To broadcast getindex for dicts, the following method is useful

(..)(x::AbstractDict,i...) = getindex.(Ref(x), i...)
mydict..mykeys # now works
3 Likes

The problem with this approach is that it won’t fuse with other dot calls, so you may generate unnecessary loops and temporary arrays.

Yep, that is an issue. The cases where I do want to use this syntax tend to be casual scripting where ugly code is worse than the performance hit though. An alternative approach is to find a single-character operator that when prefixed with a . lowers to broadcast, such as

julia> Meta.@lower a.~b
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─ %1 = Base.broadcasted(~, a, b)
│   %2 = Base.materialize(%1)
└──      return %2
))))

and define this operator to do regular getindex. Unfortunately, ... is already taken so it is not interpreted as broadcasting of the .. operator

Wow, this is very creative. One way to avoid generating temporary arrays is to use broadcasted:

using Base.Broadcast: broadcasted
(..)(x, i...) = broadcasted(getindex, x, i...)
(..)(x, i::Union{Symbol, String}) = broadcasted(getfield, x, i)

Although you have to write something like (a..2) .+ 1 and (b..:re) .+ 1. You also need to write identity.(a..2) or copy(a..2) to materialize it…

1 Like

What packages resp. community standards/solutions/notations do we currently have for broadcasted getindex on arrays of arrays (to do getindex on the inner arrays)? So, equivalents to broadcast(getindex, someAoA, Ref(some_idx_or_idx_range))?

I’m aware that the topic is under discussion as a language feature, but I’m curious which approaches are in use “in the wild” right now.