Proposal: function for mutating operators?

You can use this abomination:

using SparseArrays

struct SparseMatrixBuilder{T}
    a::Int
    b::Int
    is::Vector{Int}
    js::Vector{Int}
    vs::Vector{T}
    SparseMatrixBuilder{T}(a, b) where T = new{T}(a, b, Int[], Int[], T[])
end

struct SparseMatrixBuilderScalarIndexView{T}
    builder::SparseMatrixBuilder{T}
    i::Int
    j::Int
end

function Base.getindex(builder::SparseMatrixBuilder, i::Int, j::Int)
    return SparseMatrixBuilderScalarIndexView(builder, i, j)
end

struct SparseMatrixBuilderNewAddition{T}
    builder_view::SparseMatrixBuilderScalarIndexView{T}
    v::T
end

function Base.:(+)(builder_view::SparseMatrixBuilderScalarIndexView{T}, v::T) where T
    return SparseMatrixBuilderNewAddition(builder_view, v)
end

function Base.:(-)(builder_view::SparseMatrixBuilderScalarIndexView{T}, v::T) where T
    return SparseMatrixBuilderNewAddition(builder_view, -v)
end

function Base.setindex!(builder::SparseMatrixBuilder{T}, v::SparseMatrixBuilderNewAddition{T}, i::Int, j::Int) where T
    if !(i == v.builder_view.i && j == v.builder_view.j && builder === v.builder_view.builder)
        error("good error message")
    end
    if !(1 <= i <= builder.a && 1 <= j <= builder.b)
        throw(BoundsError(builder, (i, j)))
    end
    push!(builder.is, i)
    push!(builder.js, j)
    push!(builder.vs, v.v)
    return v
end

to_matrix(A::SparseMatrixBuilder) = sparse(A.is, A.js, A.vs, A.a, A.b)
julia> builder = SparseMatrixBuilder{Float64}(2, 2);

julia> builder[1, 1] += 1.2;

julia> builder[2, 2] += 3.4;

julia> builder[1, 2] -= 5.6;

julia> to_matrix(builder)
2×2 SparseMatrixCSC{Float64, Int64} with 3 stored entries:
 1.2  -5.6
  ⋅    3.4

Note that builder[2, 2] works but return a “useless” object – there are no methods for it so will just give MethodError as soon as you try to use it. builder[2, 2] = 3 is a MethodError for example.

4 Likes