# Expected type promotion with user-defined type not happening

**URL:** <https://discourse.julialang.org/t/expected-type-promotion-with-user-defined-type-not-happening/29803>\
**Category:** General Usage\
**Tags:** type\
**Created:** [October 12, 2019, 12:58am UTC](https://discourse.julialang.org/t/expected-type-promotion-with-user-defined-type-not-happening/29803 "2019-10-12T00:58:41Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![jonniedie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonniedie/32/12842_2.png) [@jonniedie](https://discourse.julialang.org/u/jonniedie)\
**Post date:** [October 12, 2019, 12:58am UTC](https://discourse.julialang.org/t/expected-type-promotion-with-user-defined-type-not-happening/29803/1 "2019-10-12T00:58:41Z")

</div>

I traced a bug in some of my code down to some behavior that I don’t quite understand regarding type promotion. I’m not sure if the issue is with the MonteCarloMeasurements package I’m using or my general misunderstanding of the Julia language.

I am attempting to create an array of `Particles` variables. Since some of the elements of the array are not uncertain, I was counting on the fact that creating a combined array of `Particles` and `Float64` will automatically promote the `Float64`s to `Particles` type. Here is an example of the behavior I was expecting:

```julia
julia> using MonteCarloMeasurements

julia> [3.2, 5.6±1.0]
2-element Array{Particles{Float64,500},1}:
 3.2 ± 1.8e-15
 5.6 ± 1.0

```

Notice the `3.2` became `3.2 ± 1.8e-15`, a `Particles{Float64,500}` type. This is the behavior I expect and want to happen. Now if I define a struct with field `val` and a helper function for retrieving that `val` so I can call it’s vectorized form on an array, the array formed by the helper function is type `Array{Real,1}` when I would expect it to be an `Array{Particles{Float64,500}},1}` based off of the above promotion rule:

```julia
julia> struct Blah
       val
       end

       val(arg::Blah) = arg.val
       
       b = Blah(3.2)
       c = Blah(5.6±1.0)

 julia> val.([b, c])
2-element Array{Real,1}:
 3.2       
 5.61 ± 1.0

```

This also happens if I use a list comprehension instead of a vectorized function call. If I create the array manually, though, it acts like I expect it to:

```julia
julia> [b.a, c.a]
2-element Array{Particles{Float64,500},1}:
 3.2 ± 1.8e-15
 5.61 ± 1.0  

```

What am I missing?

(Also, as a side note: what is going on with `5.6 ± 1.0` becoming `5.61 ± 1.0`?)

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [October 12, 2019, 5:08am UTC](https://discourse.julialang.org/t/expected-type-promotion-with-user-defined-type-not-happening/29803/2 "2019-10-12T05:08:05Z")

</div>

I will have a look at the promotion issue and see if there is anything I can do about it. As for the 5.60 turning into 5.61, this is due to the monte-carlo sampling of the distribution being non-deterministic. The variance of the mean in this sampling decreases as 1/N where N is the number of particles, in this case the std of the mean is 1/\sqrt(500) \approx 0.045. However, due to the systematic sampling employed, the std will actually decrease as \sim 1/N which is why it’s less than expected. See further analysis [here](https://github.com/baggepinnen/MonteCarloMeasurements.jl#monte-carlo-sampling-properties)

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [October 12, 2019, 5:16am UTC](https://discourse.julialang.org/t/expected-type-promotion-with-user-defined-type-not-happening/29803/3 "2019-10-12T05:16:00Z")

</div>

The promotion issue is due to julia not knowing how to promote two `Blah` with different field types.

If you parameterize your `Blah` and define the proper promotion rule, it works out

```julia
struct Blah{T}
    val::T
end

julia> T = Base.promote_typeof(b,c)
Blah # Julia promoted to an abstract type

julia> Base.promote_rule(::Type{Blah{T}}, ::Type{Blah{S}}) where {T,S} = Blah{promote_type(T,S)} # Define the promotion rule

julia> Base.convert(::Type{Blah{T}}, b::Blah{S}) where {T,S} = Blah{T}(T(b.val)) # define a convert method

julia> T = Base.promote_typeof(b,c)
Blah{Particles{Float64,500}} # Now the promotion works as expected

julia> val.([b, c])
2-element Array{Particles{Float64,500},1}:
 3.2 ± 1.8e-15
 5.6 ± 1.0 

```

Indeed, without the promotion rule, you get the same behavior with `Float64/Int`

```julia
julia> b = Blah(1)
Blah{Int64}(1)

julia> c = Blah(1.)
Blah{Float64}(1.0)

julia> [b, c]
2-element Array{Blah,1}:
 Blah{Int64}(1)    
 Blah{Float64}(1.0)

julia> val.([b, c])
2-element Array{Real,1}:
 1  
 1.0

```

---

<div class="post-metadata">

**Author:** ![jonniedie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonniedie/32/12842_2.png) [@jonniedie](https://discourse.julialang.org/u/jonniedie)\
**Post date:** [October 12, 2019, 12:18pm UTC](https://discourse.julialang.org/t/expected-type-promotion-with-user-defined-type-not-happening/29803/4 "2019-10-12T12:18:28Z")

</div>

Ah, that makes sense. Thank you.
