# Type instability when evaluating prior distributions

**URL:** <https://discourse.julialang.org/t/type-instability-when-evaluating-prior-distributions/19489>\
**Category:** General Usage\
**Created:** [January 10, 2019, 5:22pm UTC](https://discourse.julialang.org/t/type-instability-when-evaluating-prior-distributions/19489 "2019-01-10T17:22:34Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![BenjaminBorn](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/benjaminborn/32/4910_2.png) [@BenjaminBorn](https://discourse.julialang.org/u/BenjaminBorn)\
**Post date:** [January 10, 2019, 5:22pm UTC](https://discourse.julialang.org/t/type-instability-when-evaluating-prior-distributions/19489/1 "2019-01-10T17:22:34Z")

</div>

When looking at the `@code_warntype` output of the following function

```julia
using Distributions
const priors = [Normal(0, 1), Beta(2, 5)]
const pars = [2.0, 0.9]

function prioreval_test(priors, pars::Vector{Float64})

    if all(insupport.(priors, pars))
        log_priorval = sum(logpdf.(priors, pars))
        alarm = false
    else
        log_priorval = -Inf
        alarm = true
    end

    return log_priorval, alarm
end

@code_warntype prioreval_test(priors, pars)

```

I find that it is not type stable

```julia
Body::Tuple{Any,Bool}
3 1 ── %1 = (Base.arraysize)(priors, 1)::Int64 │╻╷╷╷╷╷ materialize
   │ %2 = (Base.slt_int)(%1, 0)::Bool ││╻╷╷╷ instantiate
   │ %3 = (Base.ifelse)(%2, 0, %1)::Int64 │││┃││││││ combine_axes
   │ %4 = %new(Base.OneTo{Int64}, %3)::Base.OneTo{Int64} ││││┃││││ broadcast_axes
   │ %5 = (Base.arraysize)(pars, 1)::Int64 │││││╻╷╷ broadcast_axes
   │ %6 = (Base.slt_int)(%5, 0)::Bool ││││││╻╷╷╷ axes
   │ %7 = (Base.ifelse)(%6, 0, %5)::Int64 │││││││┃│││ map
   │ %8 = %new(Base.OneTo{Int64}, %7)::Base.OneTo{Int64} ││││││││┃│ Type
   │ %9 = (%7 === %3)::Bool │││││╻╷╷╷╷ _bcs
   │ %10 = (Base.and_int)(true, %9)::Bool ││││││╻ _bcs1
   └─── goto #3 if not %10 │││││││┃ _bcsm
   2 ── goto #4 ││││││││       
   3 ── %13 = (%3 === 1)::Bool ││││││││╻ ==
   └─── goto #4 ││││││││       
   4 ┄─ %15 = φ (#2 => %10, #3 => %13)::Bool │││││││        
   └─── goto #6 if not %15 │││││││        
   5 ── goto #12 │││││││        
   6 ── %18 = (%3 === %7)::Bool ││││││││╻╷ ==
   │ %19 = (Base.and_int)(true, %18)::Bool │││││││││╻ &
   └─── goto #8 if not %19 ││││││││       
   7 ── goto #9 ││││││││       
   8 ── %22 = (%7 === 1)::Bool ││││││││╻ ==
   └─── goto #9 ││││││││       
   9 ┄─ %24 = φ (#7 => %19, #8 => %22)::Bool │││││││        
   └─── goto #11 if not %24 │││││││        
   10 ─ goto #12 │││││││        
   11 ─ %27 = %new(Base.DimensionMismatch, "arrays could not be broadcast to a common size")::DimensionMismatch │││││││╻ Type
   │ (Base.Broadcast.throw)(%27) │││││││        
   └─── $(Expr(:unreachable)) │││││││        
   12 ┄ %30 = φ (#5 => %7, #10 => %3)::Int64 ││││││         
   │ %31 = φ (#5 => %7, #10 => %3)::Int64 ││││││         
   │ %32 = φ (#5 => %7, #10 => %3)::Int64 ││││││         
   │ %33 = φ (#5 => %7, #10 => %3)::Int64 ││││││         
   │ %34 = φ (#5 => %8, #10 => %4)::Base.OneTo{Int64} ││││││         
   │ %35 = (Core.tuple)(%34)::Tuple{Base.OneTo{Int64}} ││││││         
   └─── goto #13 ││││││         
   13 ─ goto #14 │││││          
   14 ─ goto #15 ││││           
   15 ─ goto #16 ││╻ instantiate
   16 ─ %40 = (Base.arraysize)(priors, 1)::Int64 │││╻╷╷╷╷╷╷╷ preprocess
   │ %41 = (Base.slt_int)(%40, 0)::Bool ││││╻╷╷╷ preprocess_args
   │ %42 = (Base.ifelse)(%41, 0, %40)::Int64 │││││┃││││││││ preprocess
   │ %43 = (%42 === 1)::Bool ││││││╻╷╷ extrude
   │ %44 = (Base.not_int)(%43)::Bool │││││││╻ newindexer
   │ %45 = (Core.tuple)(%44)::Tuple{Bool} ││││││││┃│ shapeindexer
   │ %46 = (Core.tuple)(1)::Tuple{Int64} │││││││││┃ _newindexer
   │ %47 = %new(Base.Broadcast.Extruded{Array{Distribution{Univariate,Continuous},1},Tuple{Bool},Tuple{Int64}}, priors, %45, %46)::Base.Broadcast.Extruded{Array{Distribution{Univariate,Continuous},1},Tuple{Bool},Tuple{Int64}}
   │ %48 = (Base.arraysize)(pars, 1)::Int64 ││││││╻╷╷╷╷╷ preprocess
   │ %49 = (Base.slt_int)(%48, 0)::Bool │││││││╻╷╷╷ extrude
   │ %50 = (Base.ifelse)(%49, 0, %48)::Int64 ││││││││┃││││││ newindexer
   │ %51 = (%50 === 1)::Bool │││││││││╻╷╷ shapeindexer
   │ %52 = (Base.not_int)(%51)::Bool ││││││││││╻ _newindexer
   │ %53 = (Core.tuple)(%52)::Tuple{Bool} │││││││││││    
   │ %54 = (Core.tuple)(1)::Tuple{Int64} │││││││││││    
   │ %55 = %new(Base.Broadcast.Extruded{Array{Float64,1},Tuple{Bool},Tuple{Int64}}, pars, %53, %54)::Base.Broadcast.Extruded{Array{Float64,1},Tuple{Bool},Tuple{Int64}}
   │ %56 = (Core.tuple)(%47, %55)::Tuple{Base.Broadcast.Extruded{Array{Distribution{Univariate,Continuous},1},Tuple{Bool},Tuple{Int64}},Base.Broadcast.Extruded{Array{Float64,1},Tuple{Bool},Tuple{Int64}}}
   │ %57 = %new(Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1},Tuple{Base.OneTo{Int64}},typeof(insupport),Tuple{Base.Broadcast.Extruded{Array{Distribution{Univariate,Continuous},1},Tuple{Bool},Tuple{Int64}},Base.Broadcast.Extruded{Array{Float64,1},Tuple{Bool},Tuple{Int64}}}}, Distributions.insupport, %56, %35)::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1},Tuple{Base.OneTo{Int64}},typeof(insupport),Tuple{Base.Broadcast.Extruded{Array{Distribution{Univariate,Continuous},1},Tuple{Bool},Tuple{Int64}},Base.Broadcast.Extruded{Array{Float64,1},Tuple{Bool},Tuple{Int64}}}}
   │ %58 = (Base.slt_int)(%30, 1)::Bool │││╻╷╷╷ iterate
   └─── goto #18 if not %58 ││││           
   17 ─ goto #19 ││││           
   18 ─ goto #19 ││││           
   19 ┄ %62 = φ (#17 => true, #18 => false)::Bool │││            
   │ %63 = φ (#18 => 1)::Int64 │││            
   │ %64 = φ (#18 => 1)::Int64 │││            
   └─── goto #21 if not %62 │││            
   20 ─ %66 = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Any,1}, svec(Any, Int64), :(:ccall), 2, Array{Any,1}, :(%31), :(%31)))::Array{Any,1}lar
   └─── goto #31 │││            
   21 ─ goto #26 if not false │││╻ getindex
   22 ─ %69 = (Base.sle_int)(1, %63)::Bool ││││╻╷╷ checkbounds
   │ %70 = (Base.sle_int)(%63, %32)::Bool │││││┃││ checkbounds_indices
   │ %71 = (Base.and_int)(%69, %70)::Bool ││││││╻ checkindex
   │ %72 = (Base.and_int)(%71, true)::Bool ││││││╻ &
   └─── goto #24 if not %72 │││││          
   23 ─ goto #25 │││││          
   24 ─ %75 = Base.throw_boundserror::typeof(Base.throw_boundserror) │││││          
   │ %76 = (Core.tuple)(%63)::Tuple{Int64} │││││          
   │ invoke %75(%57::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1},Tuple{Base.OneTo{Int64}},typeof(insupport),Tuple{Base.Broadcast.Extruded{Array{Distribution{Univariate,Continuous},1},Tuple{Bool},Tuple{Int64}},Base.Broadcast.Extruded{Array{Float64,1},Tuple{Bool},Tuple{Int64}}}}, %76::Tuple{Int64})
   └─── $(Expr(:unreachable)) │││││          
   25 ┄ nothing │              
   26 ─ %80 = (Base.Broadcast.ifelse)(%44, %63, 1)::Int64 ││││╻╷╷╷ _broadcast_getindex
   │ %81 = (Base.arrayref)(false, priors, %80)::Distribution{Univariate,Continuous} │││││╻ _getindex
   │ %82 = (Base.Broadcast.ifelse)(%52, %63, 1)::Int64 ││││││╻╷ _getindex
   │ %83 = (Base.arrayref)(false, pars, %82)::Float64 │││││││╻ _broadcast_getindex
   │ %84 = (Distributions.insupport)(%81, %83)::Any │││││╻ _broadcast_getindex_evalf
   └─── goto #27 ││││           
   27 ─ %86 = (Base.Broadcast.typeof)(%84)::DataType ││╻ copy
   │ %87 = (isa)(%86, Type{Bool})::Bool │││            
   └─── goto #29 if not %87 │││            
   28 ─ %89 = invoke BitArray{1}($(QuoteNode(array initializer with undefined values))::UndefInitializer, %33::Int64)::BitArray{1}╷╷ similar
   └─── goto #30 │││            
   29 ─ %91 = (Base.Broadcast.similar)(%57, %86)::Any │││            
   └─── goto #30 │││            
   30 ┄ %93 = φ (#28 => %89, #29 => %91)::Any │││            
   │ (Base.setindex!)(%93, %84, %63) │││            
   │ %95 = (Base.Broadcast.copyto_nonleaf!)(%93, %57, %34, %64, 1)::Any │││            
   └─── goto #31 │││            
   31 ┄ %97 = φ (#20 => %66, #30 => %95)::Any ││             
   └─── goto #32 ││             
   32 ─ %99 = (Main.all)(%97)::Any │              
   └─── goto #65 if not %99 │              
4 33 ─ %101 = (Base.arraysize)(priors, 1)::Int64 │╻╷╷╷╷╷ materialize
   │ %102 = (Base.slt_int)(%101, 0)::Bool ││╻╷╷╷ instantiate
   │ %103 = (Base.ifelse)(%102, 0, %101)::Int64 │││┃││││││ combine_axes
   │ %104 = %new(Base.OneTo{Int64}, %103)::Base.OneTo{Int64} ││││┃││││ broadcast_axes
   │ %105 = (Base.arraysize)(pars, 1)::Int64 │││││╻╷╷ broadcast_axes
   │ %106 = (Base.slt_int)(%105, 0)::Bool ││││││╻╷╷╷ axes
   │ %107 = (Base.ifelse)(%106, 0, %105)::Int64 │││││││┃│││ map
   │ %108 = %new(Base.OneTo{Int64}, %107)::Base.OneTo{Int64} ││││││││┃│ Type
   │ %109 = (%107 === %103)::Bool │││││╻╷╷╷╷ _bcs
   │ %110 = (Base.and_int)(true, %109)::Bool ││││││╻ _bcs1
   └─── goto #35 if not %110 │││││││┃ _bcsm
   34 ─ goto #36 ││││││││       
   35 ─ %113 = (%103 === 1)::Bool ││││││││╻ ==
   └─── goto #36 ││││││││       
   36 ┄ %115 = φ (#34 => %110, #35 => %113)::Bool │││││││        
   └─── goto #38 if not %115 │││││││        
   37 ─ goto #44 │││││││        
   38 ─ %118 = (%103 === %107)::Bool ││││││││╻╷ ==
   │ %119 = (Base.and_int)(true, %118)::Bool │││││││││╻ &
   └─── goto #40 if not %119 ││││││││       
   39 ─ goto #41 ││││││││       
   40 ─ %122 = (%107 === 1)::Bool ││││││││╻ ==
   └─── goto #41 ││││││││       
   41 ┄ %124 = φ (#39 => %119, #40 => %122)::Bool │││││││        
   └─── goto #43 if not %124 │││││││        
   42 ─ goto #44 │││││││        
   43 ─ %127 = %new(Base.DimensionMismatch, "arrays could not be broadcast to a common size")::DimensionMismatch │││││││╻ Type
   │ (Base.Broadcast.throw)(%127) │││││││        
   └─── $(Expr(:unreachable)) │││││││        

...

%new(Base.Broadcast.Extruded{Array{Distribution{Univariate,Continuous},1},Tuple{Bool},Tuple{Int64}}, priors, %145, %146)::Base.Broadcast.Extruded{Array{Distribution{Univariate,Continuous},1},Tuple{Bool},Tuple{Int64}}
   │ %148 = (Base.arraysize)(pars, 1)::Int64 ││││││╻╷╷╷╷╷ preprocess
   │ %149 = (Base.slt_int)(%148, 0)::Bool │││││││╻╷╷╷ extrude
   │ %150 = (Base.ifelse)(%149, 0, %148)::Int64 ││││││││┃││││││ newindexer
   │ %151 = (%150 === 1)::Bool │││││││││╻╷╷ shapeindexer
   │ %152 = (Base.not_int)(%151)::Bool ││││││││││╻ _newindexer
   │ %153 = (Core.tuple)(%152)::Tuple{Bool} │││││││││││    
   │ %154 = (Core.tuple)(1)::Tuple{Int64} │││││││││││    
   │ %155 = %new(Base.Broadcast.Extruded{Array{Float64,1},Tuple{Bool},Tuple{Int64}}, pars, %153, %154)::Base.Broadcast.Extruded{Array{Float64,1},Tuple{Bool},Tuple{Int64}}
   │ %156 = (Core.tuple)(%147, %155)::Tuple{Base.Broadcast.Extruded{Array{Distribution{Univariate,Continuous},1},Tuple{Bool},Tuple{Int64}},Base.Broadcast.Extruded{Array{Float64,1},Tuple{Bool},Tuple{Int64}}}
   │ %157 = %new(Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1},Tuple{Base.OneTo{Int64}},typeof(logpdf),Tuple{Base.Broadcast.Extruded{Array{Distribution{Univariate,Continuous},1},Tuple{Bool},Tuple{Int64}},Base.Broadcast.Extruded{Array{Float64,1},Tuple{Bool},Tuple{Int64}}}}, Distributions.logpdf, %156, %135)::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1},Tuple{Base.OneTo{Int64}},typeof(logpdf),Tuple{Base.Broadcast.Extruded{Array{Distribution{Univariate,Continuous},1},Tuple{Bool},Tuple{Int64}},Base.Broadcast.Extruded{Array{Float64,1},Tuple{Bool},Tuple{Int64}}}}
   │ %158 = (Base.slt_int)(%130, 1)::Bool │││╻╷╷╷ iterate
   └─── goto #50 if not %158 ││││           
   49 ─ goto #51 ││││           
   50 ─ goto #51 ││││           
   51 ┄ %162 = φ (#49 => true, #50 => false)::Bool │││            
   │ %163 = φ (#50 => 1)::Int64 │││            
   │ %164 = φ (#50 => 1)::Int64 │││            
   └─── goto #53 if not %162 │││            
   52 ─ %166 = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Any,1}, svec(Any, Int64), :(:ccall), 2, Array{Any,1}, :(%131), :(%131)))::Array{Any,1}r
   └─── goto #63 │││            
   53 ─ goto #58 if not false │││╻ getindex
   54 ─ %169 = (Base.sle_int)(1, %163)::Bool ││││╻╷╷ checkbounds
   │ %170 = (Base.sle_int)(%163, %132)::Bool │││││┃││ checkbounds_indices
   │ %171 = (Base.and_int)(%169, %170)::Bool ││││││╻ checkindex
   │ %172 = (Base.and_int)(%171, true)::Bool ││││││╻ &
   └─── goto #56 if not %172 │││││          
   55 ─ goto #57 │││││          
   56 ─ %175 = Base.throw_boundserror::typeof(Base.throw_boundserror) │││││          
   │ %176 = (Core.tuple)(%163)::Tuple{Int64} │││││          
   │ invoke %175(%157::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1},Tuple{Base.OneTo{Int64}},typeof(logpdf),Tuple{Base.Broadcast.Extruded{Array{Distribution{Univariate,Continuous},1},Tuple{Bool},Tuple{Int64}},Base.Broadcast.Extruded{Array{Float64,1},Tuple{Bool},Tuple{Int64}}}}, %176::Tuple{Int64})
   └─── $(Expr(:unreachable)) │││││          
   57 ┄ nothing │              
   58 ─ %180 = (Base.Broadcast.ifelse)(%144, %163, 1)::Int64 ││││╻╷╷╷ _broadcast_getindex
   │ %181 = (Base.arrayref)(false, priors, %180)::Distribution{Univariate,Continuous} │││││╻ _getindex
   │ %182 = (Base.Broadcast.ifelse)(%152, %163, 1)::Int64 ││││││╻╷ _getindex
   │ %183 = (Base.arrayref)(false, pars, %182)::Float64 │││││││╻ _broadcast_getindex
   │ %184 = (Distributions.logpdf)(%181, %183)::Any │││││╻ _broadcast_getindex_evalf
   └─── goto #59 ││││           
   59 ─ %186 = (Base.Broadcast.typeof)(%184)::DataType ││╻ copy
   │ %187 = (isa)(%186, Type{Bool})::Bool │││            
   └─── goto #61 if not %187 │││            
   60 ─ %189 = invoke BitArray{1}($(QuoteNode(array initializer with undefined values))::UndefInitializer, %133::Int64)::BitArray{1}╷ similar
   └─── goto #62 │││            
   61 ─ %191 = (Base.Broadcast.similar)(%157, %186)::Any │││            
   └─── goto #62 │││            
   62 ┄ %193 = φ (#60 => %189, #61 => %191)::Any │││            
   │ (Base.setindex!)(%193, %184, %163) │││            
   │ %195 = (Base.Broadcast.copyto_nonleaf!)(%193, %157, %134, %164, 1)::Any │││            
   └─── goto #63 │││            
   63 ┄ %197 = φ (#52 => %166, #62 => %195)::Any ││             
   └─── goto #64 ││             
   64 ─ %199 = (Main.sum)(%197)::Any │              
5 └─── goto #66 │              
   65 ─ nothing │              
11 66 ┄ %202 = φ (#64 => %199, #65 => -Inf)::Any │              
   │ %203 = φ (#64 => false, #65 => true)::Bool │              
   │ %204 = (Core.tuple)(%202, %203)::Tuple{Any,Bool} │              
   └─── return %204  

```

I can force the output to Float64 by setting the last line to ` return log_priorval::Float64, alarm` but I would like to understand where the type instability is coming from. It has to be the if/else statement but I don’t see why as `typeof(Inf)`is also `Float64`.

Thanks for your help.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 10, 2019, 7:00pm UTC](https://discourse.julialang.org/t/type-instability-when-evaluating-prior-distributions/19489/2 "2019-01-10T19:00:11Z")

</div>

Since `priors` is a vector of heterogeneous elements (`Normal` and `Beta`), it does not have a concrete element type. The quickest fix is probably using tuples, and then I would do the same for `pars`. Eg

```julia
using Distributions
const priors = (Normal(0, 1), Beta(2, 5))
const pars = (2.0, 0.9)

function prioreval_test(priors, pars)
    if all(insupport.(priors, pars))
        log_priorval = sum(logpdf.(priors, pars))
        alarm = false
    else
        log_priorval = -Inf
        alarm = true
    end

    return log_priorval, alarm
end

@code_warntype prioreval_test(priors, pars)

```

That said, I would recommend you use `NamedTuple`s for parameters, which can avoid order mixups.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 10, 2019, 7:01pm UTC](https://discourse.julialang.org/t/type-instability-when-evaluating-prior-distributions/19489/3 "2019-01-10T19:01:54Z")

</div>

also, note that outside the support, you get `-Inf` automatically for the `logpdf`, eg

```julia
julia> logpdf(Beta(0.1, 0.7), -9)
-Inf

```

so testing for it separately may not be necessary.

---

<div class="post-metadata">

**Author:** ![BenjaminBorn](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/benjaminborn/32/4910_2.png) [@BenjaminBorn](https://discourse.julialang.org/u/BenjaminBorn)\
**Post date:** [January 10, 2019, 7:53pm UTC](https://discourse.julialang.org/t/type-instability-when-evaluating-prior-distributions/19489/4 "2019-01-10T19:53:04Z")

</div>

Thanks Tamas, that’s very helpful. Just two quick follow-ups so that I understand this better.

1. (a) Why does it matter for type stability that the `priors` vector contains two different distributions? The outcome of `sum(logpdf.())` will always be a `Float64` scalar. (b) I tried using a tuple for `priors` and a vector for `pars` but that has the same issues as two vectors. Is that just the interplay between the two that then screws up type inference?

2. I can easily construct the priors as a tuple (I use Flatten.jl and that allows to flatten the struct into tuple or vector). But the parameter vector comes directly from the optimizer (using Optim.jl). If I understand it correctly, Optim only outputs (and accepts) arrays. Of course I could transform the vector into a tuple but is there a smarter way?

Again, thanks for your help.

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [January 11, 2019, 12:22am UTC](https://discourse.julialang.org/t/type-instability-when-evaluating-prior-distributions/19489/5 "2019-01-11T00:22:18Z")

</div>

```julia
using Distributions
const priors = [Normal(0, 1), Beta(2, 5)]
const pars = [2.0, 0.9]

struct MyDegenerateDistribution <: Distributions.ContinuousUnivariateDistribution end
Distributions.logpdf(::MyDegenerateDistribution, ::Float64) = big(42)
Distributions.insupport(::MyDegenerateDistribution, ::Real) = true

push!(priors, MyDegenerateDistribution())
push!(pars, Inf)

```

Then,

```julia
julia> isa.(priors, Distributions.ContinuousUnivariateDistribution)
3-element BitArray{1}:
 true
 true
 true

julia> prioreval_test(priors, pars)
(33.1665579608234732944538336596451699733734130859375, false)

julia> typeof(ans)
Tuple{BigFloat,Bool}

```

Our answer is a BigFloat!  
Or,

```julia
julia> using StaticArrays

julia> Distributions.logpdf(::MyDegenerateDistribution, ::Float64) = @SMatrix randn(3,3)

julia> prioreval_test(priors, pars)
([-9.15028 -8.84753 -7.32728; -9.23861 -8.53093 -9.21451; -9.76463 -8.44686 -6.88402], false)

```

Even worse, `sum(logpdf.())` doesn’t have to be defined:

```julia
julia> Distributions.logpdf(::MyDegenerateDistribution, ::Float64) = "I don't play by the rules!"

julia> prioreval_test(priors, pars)
ERROR: MethodError: no method matching +(::Float64, ::String)
Closest candidates are:
  +(::Any, ::Any, ::Any, ::Any...) at operators.jl:521
  +(::Float64, ::Float64) at float.jl:395
  +(::AbstractFloat, ::Bool) at bool.jl:114
  ...
Stacktrace:
 [1] add_sum(::Float64, ::String) at ./reduce.jl:21
 [2] _mapreduce(::typeof(identity), ::typeof(Base.add_sum), ::IndexLinear, ::Array{Any,1}) at ./reduce.jl:316
 [3] _mapreduce_dim at ./reducedim.jl:308 [inlined]
 [4] #mapreduce#549 at ./reducedim.jl:304 [inlined]
 [5] mapreduce at ./reducedim.jl:304 [inlined]
 [6] _sum at ./reducedim.jl:653 [inlined]
 [7] _sum at ./reducedim.jl:652 [inlined]
 [8] #sum#551 at ./reducedim.jl:648 [inlined]
 [9] sum(::Array{Any,1}) at ./reducedim.jl:648
 [10] prioreval_test(::Array{Distribution{Univariate,Continuous},1}, ::Array{Float64,1}) at ./REPL[21]:4
 [11] top-level scope at none:0

```

While it is hard to think of a good reason for anyone to do that, the compiler’s job is producing valid code. Because it is possible to do this, the compiler needs to generate code able to handle it.

In Tamas\_Papps version, with the tuples, the function can specialize one each individual types. Receiving just the Beta and Normal distribution, the compiler is guaranteed silliness like these examples aren’t possible.

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [January 11, 2019, 12:46am UTC](https://discourse.julialang.org/t/type-instability-when-evaluating-prior-distributions/19489/6 "2019-01-11T00:46:37Z")

</div>

a) Vectors are fast if they contain one type of data, otherwise data is boxed as ANY at some point which requires a lot more native code to handle (although small Union vectors are also pretty fast these days).  
b) Good to see someone using Flatten.jl for this! Tuple is the default output for this very reason - the vector is just splatted from the tuple for convenience. I should put a warning somewhere in the readme about type stability. For the pars it seems strange that a vector creates type instability, as it’s all Float64. But it’s probably something to do with how broadcast is compiled when given a tuple and a vector. My answer to this kind of type instability is often to use recursion instead of for loops or broadcast. But you could just splat the vector to a tuple here.

As for Optim only working with Vectors it’s a broader problem I would also like to see addressed more often - accepting tuples instead of vectors for parameter lists. This would mean lists of prior distributions and parameters with different Unitful units could be type stable.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 11, 2019, 9:13am UTC](https://discourse.julialang.org/t/type-instability-when-evaluating-prior-distributions/19489/7 "2019-01-11T09:13:27Z")

</div>

> [@BenjaminBorn](#):
>
> Of course I could transform the vector into a tuple but is there a smarter way?

I wrote TransformVariables precisely for this. It can also transform an unconstrained real to a constrained subset, eg

```julia
using TransformVariables
t = as((x = asℝ, a = as𝕀))
dimension(t)
t([0.1, 0.2])

```

---

<div class="post-metadata">

**Author:** ![BenjaminBorn](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/benjaminborn/32/4910_2.png) [@BenjaminBorn](https://discourse.julialang.org/u/BenjaminBorn)\
**Post date:** [January 11, 2019, 12:45pm UTC](https://discourse.julialang.org/t/type-instability-when-evaluating-prior-distributions/19489/8 "2019-01-11T12:45:58Z")

</div>

Thanks everyone! This helped me a lot in understanding the issue.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [March 26, 2020, 6:43pm UTC](https://discourse.julialang.org/t/type-instability-when-evaluating-prior-distributions/19489/10 "2020-03-26T18:43:44Z")

</div>

Please read [my reply above](https://discourse.julialang.org/t/type-instability-when-evaluating-prior-distributions/19489/2) again, the answer is at the beginning.

Also, please do invest some effort into reading answers in a topic before pinging people directly.
