Poll: Do you use convenience functions such as `zeros` to construct OffsetArrays?

Recently, it has been pointed out that the type-piracy committed by OffsetArrays can lead to unexpected results. In particular, OffsetArrays provides the convenience method zeros(::Union{Integer, AbstractUnitRange}...) to construct an offset array of zeros, e.g. as

julia> zeros(3:4)
2-element OffsetArray(::Vector{Float64}, 3:4) with eltype Float64 with indices 3:4:
 0.0
 0.0

Similar methods are added to ones, trues, falses and fill to return OffsetArrays, so, e.g.,

julia> fill(4, 5:7)
3-element OffsetArray(::Vector{Int64}, 5:7) with eltype Int64 with indices 5:7:
 4
 4
 4

Despite the convenience offered by such methods, they commit type-piracy, and we’re in the middle of deciding whether such methods should be retained in the next breaking release of OffsetArrays. I would like to hear from users of OffsetArrays if they feel that these should be retained.

Do you use any of the convenience functions zeros, ones, trues, falses or fill to construct OffsetArrays?

  • I don’t use OffsetArrays
  • I use OffsetArrays, but don’t use these methods
  • I use OffsetArrays, and I have used these methods before, but I don’t care either way
  • I use OffsetArrays, and find these methods quite useful

0 voters

My suggestion is that OffsetArrays should not extend Base.zeros. There could be an OffsetArrays.zeros though that is not exported. If someone wants OffsetArrays.zeros they should explicitly import it.

However, I really think something like this should be the syntax: OffsetArray{Int}(zeroinit, 3:4) where const zeroinit = Fill(0).

1 Like

I quite like this syntax, and it is available as well. My one concern is that, in analogy with the Base method

julia> Array{Missing,2}(missing, 2, 2)
2×2 Matrix{Missing}:
 missing  missing
 missing  missing

should Fill(0) be the scalar element, or should it be 0? I suppose a special FillInitializer type may declare that the element is the scalar. I wonder why such an initializer isn’t available in Base?

Because you run into the same problem as with the existing fill (which I’m sure you can find discussion & issues about):

julia> mutable struct A
           x::Int
       end

julia> arr = fill(A(1), 3)
3-element Vector{A}:
 A(1)
 A(1)
 A(1)

julia> arr[1] === arr[2]
true

Should that FillInitializer copy or not?

I’d say that it shouldn’t. A FillInitializer should not behave differently from fill. It’ll only facilitate dispatch while constructing arrays

Edit: I found an issue with some discussion. In any case, OffsetArrays.jl probably isn’t the right place to introduce a filling initializer.

1 Like