# A = A .+ 1 yields different type than A .+= 1; why?

**URL:** https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189
**Category:** General Usage
**Tags:** type
**Created:** [April 18, 2025, 4:03pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189 "2025-04-18T16:03:52Z")
**Posts on this page:** 13
**Page:** 1

<div class="post-metadata">

### Author: ![Joris\_Pinkse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joris_pinkse/32/216398_2.png) [@Joris\_Pinkse](https://discourse.julialang.org/u/Joris_Pinkse)
#### Post date: [April 18, 2025, 4:03pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189/1 "2025-04-18T16:03:53Z")

</div>

In the code below, I had expected the three additions at the end to produce the same type. Plus, given that the constructor of Share should throw an error when its argument is outside the range 0 to 1, I would not have expected to see a matrix of type Share with values outside that range.

What is going on?

```julia
julia> mutable struct Share{𝒯<:Real} <:Real
           x :: 𝒯
       end

julia> function Share( x :: 𝒯 ) where 𝒯<:Real
           @assert 0 ≤ x ≤ 1 "Shares must be between 0 and 1 inclusive"
           return Share{𝒯}( x )
       end
Share

julia> Base.show( io :: IO, x :: Share{𝒯} ) where 𝒯 <:Real = show( io, x.x )

julia> import Base: promote_rule, convert

julia> promote_rule( ::Type{ Share{𝒯} }, ::Type{𝒯₂} ) where {𝒯 <:Real, 𝒯₂ <:Real } = promote_type( 𝒯, 𝒯₂ )
promote_rule (generic function with 103 methods)

julia> convert( ::Type{ 𝒯₂ }, x :: Share{𝒯} ) where {𝒯 <:Real, 𝒯₂ <:Real } = 𝒯₂( x.x )
convert (generic function with 137 methods)

julia> Base.:+( x :: Share, y :: Share ) = Share( x.x + y.x )

julia> s = Share.( rand(20,3) );

julia> println( typeof( s .+ 1 ) )
Matrix{Float64}

julia> s .+= 1;

julia> println( typeof( s ) );
Matrix{Share{Float64}}

julia> s = s .+ 1;

julia> println( typeof( s ) );
Matrix{Float64}

```

---

<div class="post-metadata">

### Author: ![Paul\_Schrimpf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/paul_schrimpf/32/10134_2.png) [@Paul\_Schrimpf](https://discourse.julialang.org/u/Paul_Schrimpf)
#### Post date: [April 18, 2025, 4:38pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189/2 "2025-04-18T16:38:03Z")

</div>

If you want `s .+ 1` to be an array of `Share`s, you can change `promote_rule` to

```julia
promote_rule( ::Type{ Share{𝒯} }, ::Type{𝒯₂} ) where {𝒯 <:Real, 𝒯₂ <:Real } = Share{promote_type( 𝒯, 𝒯₂ )}

```

---

<div class="post-metadata">

### Author: ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)
#### Post date: [April 18, 2025, 4:45pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189/3 "2025-04-18T16:45:47Z")

</div>

First, note that `s .+= 1` (literally equivalent to `s .= s .+ 1` can never change the type of `s`. It simply loops through `s`, adds `1` to each entry, and stores the result into the entries of `s` (calling `convert` on them before saving to ensure they are compatible). So although the entries can change, `s` as a “binding” does not and so cannot change type.

Okay, so what is `s .+ 1`? Your `promote_rule` says that when you promote a `Share{Float64}` and `Int`, the result is a `promote_type(Float64, Int) == Float64`. So `s[i] + 1` promotes both arguments to `Float64` and then adds them, resulting in a `Float64`. This is why `s .+ 1` (which creates a new array) results in a `Matrix{Float64}`.

Writing `s = s .+ 1` (without the `.=`) does not assign the results elementwise into `s`. Instead, it computes `s .+ 1` (which we established is a `Matrix{Float64}`) and then binds that matrix to the name `s` (forgetting anything `s` might have meant before).

Okay, now to your question about the `Share` constructor and its `@assert`. When we assign `s .= s .+ 1` we compute `(s[i] + 1)::Float64` then call `convert(Share{Float64}, (s[i]+1)::Float64)` as we store it into `s[i]`. Let’s try it:

```julia-repl
julia> convert(Share{Float64}, s[1] + 1)
1.3852102999353921

julia> convert(Share, s[1] + 1)
ERROR: AssertionError: Shares must be between 0 and 1 inclusive

julia> @which convert(Share{Float64}, s[1] + 1)
convert(::Type{T}, x::Number) where T<:Number
     @ Base number.jl:7

julia> @which convert(Share, s[1] + 1)
convert(::Type{T}, x::Number) where T<:Number
     @ Base number.jl:7

```

So both hit the same fallback (probably defined as `T(x)`). If `T === Share` then that hits your constructor which throws the error. But `T === Share{Float64}` is ~~actually more specific~~ not the same thing, such that it goes right into the `struct` constructor and skips the check. Note that you yourself call `Share{T}(x)` in your `Share(x)` function, so without this behavior you would have an infinite loop there.

You might be looking for an [inner constructor](https://docs.julialang.org/en/v1/manual/constructors/#man-inner-constructor-methods).

Be aware that as a `mutable struct`, it is possible to change `(::Share).x` and such changes will not trigger any constructors (or bounds checks therein). You should consider whether a (immutable) `struct` can work for you here. It will probably be more performant anyway. If you decide to stay `mutable`, you can change `Base.setproperty!` on your type to check the input before setting an illegal value.

---

<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: [April 18, 2025, 4:46pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189/4 "2025-04-18T16:46:40Z")

</div>

I know you know this, but I think it’s probably good to reinforce the difference between renaming and in-place operations:

`A = A .+ 1` is deciding that you have a better use for the name `A`. You’ll take the object you first named `A` and throw it away in favor of a _brand new one_ whose contents are all one more than the original thing.

`A .+= 1` is doing that same operation in-place. It’s the same as `A .= A .+ 1`. It’s _updating_ the contents in the object you first named `A` without renaming it to something new.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [April 18, 2025, 5:26pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189/5 "2025-04-18T17:26:01Z")

</div>

> [@mikmoore](#):
>
> If `T === Share` then that hits your constructor which throws the error. But `T === Share{Float64}` is actually more specific

Small quibble with the wording of “specific”, there’s a likely unintentional implication of method specificity thus fallbacks. While `Share{Float64} <: Share`, they’re not type annotations for input instances in their constructor methods, so as you said, `Share{Float64}` calls won’t ever fall back to `Share` methods. Either the exact parameter or a parametric method is necessary.

```julia
julia> struct X{T}
         X{Int}() = 0
         X() = 1.0
       end

julia> X{Int}(), X()
(0, 1.0)

julia> X{Float64}()
ERROR: MethodError: no method matching X{Float64}()
...
julia> X{T}() where T = X()

julia> X{Float64}()
1.0

```

---

<div class="post-metadata">

### Author: ![Joris\_Pinkse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joris_pinkse/32/216398_2.png) [@Joris\_Pinkse](https://discourse.julialang.org/u/Joris_Pinkse)
#### Post date: [April 18, 2025, 5:31pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189/6 "2025-04-18T17:31:53Z")

</div>

Thanks Paul.

---

<div class="post-metadata">

### Author: ![Joris\_Pinkse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joris_pinkse/32/216398_2.png) [@Joris\_Pinkse](https://discourse.julialang.org/u/Joris_Pinkse)
#### Post date: [April 18, 2025, 5:33pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189/7 "2025-04-18T17:33:33Z")

</div>

Aha!

From [Overload addition assignment += operator - #2 by stevengj](https://discourse.julialang.org/t/overload-addition-assignment-operator/28836/2) I deduced that `x += y`does exactly the same thing as `x = x + y`. This appears to be an exception.

---

<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: [April 18, 2025, 5:40pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189/8 "2025-04-18T17:40:01Z")

</div>

> [@Joris\_Pinkse](#):
>
> From [Overload addition assignment += operator - #2 by stevengj](https://discourse.julialang.org/t/overload-addition-assignment-operator/28836/2) I deduced that `x += y`does exactly the same thing as `x = x + y`. This appears to be an exception.

~~This isn’t an exception, it **is** the rule. Whatever you attach on the left side of that `=` assignment is moved to the right. The dot in the `.+` doesn’t “stick around” as part of the `=` — it moves with the `+`.~~

~~Whatever you manage to plop in the box in ` x □= y` — be it one or two or more characters — is the operator and it becomes `x = x □ y`.~~

---

<div class="post-metadata">

### Author: ![Joris\_Pinkse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joris_pinkse/32/216398_2.png) [@Joris\_Pinkse](https://discourse.julialang.org/u/Joris_Pinkse)
#### Post date: [April 18, 2025, 5:42pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189/9 "2025-04-18T17:42:43Z")

</div>

Ok, I see.

If `s` is of type `Matrix{Share{Float64}}` then `s .+ ones(10,10) ` is of type `Matrix{Float64}`, which if I stick it back into `s` via `.=` does not change the type of `s`, i.e. `Matrix{Share{Float64}}` .

---

<div class="post-metadata">

### Author: ![Joris\_Pinkse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joris_pinkse/32/216398_2.png) [@Joris\_Pinkse](https://discourse.julialang.org/u/Joris_Pinkse)
#### Post date: [April 18, 2025, 5:46pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189/10 "2025-04-18T17:46:03Z")

</div>

And yes, I had not appreciated that the inner versus outer constructor distinction mattered here.

---

<div class="post-metadata">

### Author: ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)
#### Post date: [April 18, 2025, 6:11pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189/11 "2025-04-18T18:11:54Z")

</div>

> [@mbauman](#):
>
> The dot in the `.+` doesn’t “stick around” as part of the `=` — it moves with the `+`.

Wait, it does both, doesn’t it? Both sticks around and moves

```plaintext
x □= y becomes x = x □ y (reassignment)
x .□= y becomes x .= x .□ y (in-place mutation)

```

---

<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: [April 18, 2025, 6:22pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189/12 "2025-04-18T18:22:21Z")

</div>

Well that’s embarrassing! Hah. Got myself thinking the wrong way around — and that’s at the crux of this very thread! Thanks 🙂

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [April 18, 2025, 7:17pm UTC](https://discourse.julialang.org/t/a-a-1-yields-different-type-than-a-1-why/128189/13 "2025-04-18T19:17:58Z")

</div>

> [@Joris\_Pinkse](#):
>
> I had not appreciated that the inner versus outer constructor distinction mattered here.

It’s worth pointing out that enforcing invariants, the sort of instantiation conditions like `0 ≤ x ≤ 1`, are done in inner constructors because invariants by definition happen all the time; outer constructors have to call inner constructors but not vice versa, so naturally inner constructors would cover what happens all the time. If for whatever reason you only want to check for the condition in an outer constructor or other method, then you could do that.

Also just remembered, generally _don’t_ use `@assert` for invariants, unless you’re limiting it to internal code that is rigorously tested to not need it in routine usage. The docs warn that “an assert might be disabled at some optimization levels. Assert should therefore only be used as a debugging tool and not used for authentication verification…” Granted, `@assert` _currently_ isn’t turned off at any optimization levels, but that’s a pretty explicit warning about not guaranteeing this in updates. Save yourself the annoying patch in the future, enforce invariants by throwing other errors. In this case the unit interval restriction could potentially use the more specific `DomainError`, but if not, the documentation on invariants use the convenience function `error` for throwing the most generic `ErrorException`.
