Overload addition assignment += operator

Here’s an example of what I’m trying to do:

struct MyStruct
    x::Int
end

function Base.:+(s::MyStruct, a::Int)
    MyStruct(s.x + a)
end

s1 = MyStruct(5)
s2 = s1 + 3

# ------ Everything above this line works ----------

# Attempting to create the following function produces:
#  ERROR: UndefVarError: += not defined
function Base.:+=(s::MyStruct, a::Int)
    s.x + a
    s
end

s1 += 1

Wondering if this is possible. Seems that += is treated differently than other operators. It can’t be found in REPL help.

help?> +=
search:

Couldn't find +=

x += y is a synonym for x = x + y in Julia, so you can’t overload it.

1 Like

Thanks.
There are some situations where it would be nice to modify a struct in-place, rather than create a copy.
But for those situations, I guess I’ll just create a slightly less-elegant add! instead.

You could just do s.x += 1.

1 Like

The MyStruct example was a trivial MWE.
The real use case involves updating many (but not all) fields in less straightforward ways.

Edit:
The specific use case is to overload the *= for Factorization so you can multiply factorizations together.

julia> f1 = Primes.factor(25)
5^2

julia> f2 = Primes.factor(36)
2^2 * 3^2

# ---- Would like to add the below capabilities -------
julia> f1 *= f2
2^2 * 3^2 * 5^2

# Looks like I'll have to settle for
julia> mul!(f1, f2)
2^2 * 3^2 * 5^2

+= or *= are not inplace operators (the LHS variable is assigned to and the object is not mutated). .+= and .*= are.

I assume .+= is not overloadable either.

Broadcast machinery can be overriden.

Wouldn’t this be type piracy?

Edit: well, unless you plan to contribute this functionality to the package itself, I guess.

You could try this package

It aims at helping with mutation of immutable structs

1 Like

Would be good to fix this if anyone feels like patching the docs:

1 Like

For an in-place calculation of a mutable struct (which I’d also like) it would be good to have +=! as a method.

I assume .+= is not overloadable either.

Unfortunately not. For that kind of questions, use

julia> Meta.@lower a .+= b
:($(Expr(:thunk, CodeInfo(
1 ─ %1 = (Base.broadcasted)(+, a, b)
│   %2 = (Base.materialize!)(a, %1)
└──      return %2
))))

So you would need to write a materialize! function that checks whether the dest is identical to one of the arguments of the broadcast, but you cannot dispatch on that.

add! or mul! is probably the way to go, with respect to naming.

1 Like

What about ⇷? It looks kind of like a combination +-assignment operator.

julia> mutable struct Foo{T}
           a::T
       end

julia> function (⇷)(a::Foo{T}, b::T) where {T}
           a.a += b
       end
⇷ (generic function with 1 method)

julia> a = Foo(3)
Foo{Int64}(3)

julia> a ⇷ 3
6

julia> a ⇷ 3
9

julia> a ⇷ 3
12

julia> a
Foo{Int64}(12)

Type it with \nvleftarrow + TAB. It has an arrow’s precedence, which quick tests seems to suggest is lower than +/-. I think the prec-names variable lists operator types in order from lowest to highest:

(define prec-names '(prec-assignment
                     prec-pair prec-conditional prec-arrow prec-lazy-or prec-lazy-and prec-comparison
                     prec-pipe< prec-pipe> prec-colon prec-plus prec-times prec-rational prec-bitshift
                     prec-power prec-decl prec-dot))

For reference, here are all the operators, and here are the unicode inputs and how to type them.