# Output the same type of struct in function

**URL:** <https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218>\
**Category:** New to Julia\
**Created:** [August 6, 2019, 2:11pm UTC](https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218 "2019-08-06T14:11:13Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![mrVeng](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrveng/32/8836_2.png) [@mrVeng](https://discourse.julialang.org/u/mrVeng)\
**Post date:** [August 6, 2019, 2:11pm UTC](https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218/1 "2019-08-06T14:11:14Z")

</div>

Hi everyone,

I have a questions regarding return types in functions. In principle, I have 2 similar structs (one is bigger, one smaller) defined, and for convenience, I want to auto-fill some fields with default values if they are not explicitly stated - I did this via methods. A short version is given here:

```julia
using Distributions

abstract type PLAN{A<:VariateForm} end

struct PLAN1{A,B} <: PLAN{A}
    a::Vector{Distribution{A}}
    b::Vector{B}
    PLAN1{A,B}(a,b) where {A,B} = new(a,b)
end
struct PLAN2{A,B} <: PLAN{A}
    a::Vector{Distribution{A}}
    b::Vector{B}
    c::Dict{Symbol, Vector{C} where {C} }
    PLAN2{A,B}(a,b,c) where {A,B,C} = new(a,b,c)
end

#####################################
#Methods
PLAN1(a::AbstractVector{<:Distribution{A}}, b::AbstractVector{B}) where {A,B} = PLAN1{A,B}(a,b)
PLAN2(a::AbstractVector{<:Distribution{A}}, b::AbstractVector{B}, c::Dict{Symbol, <:AbstractVector{C} where {C} }) where {A,B} = PLAN2{A,B}(a,b,c)
#PlAN2 without c field gets as default empty set
PLAN2(a::AbstractVector{<:Distribution{A}}, b::AbstractVector{B}) where {A,B} = PLAN2(a, b, Dict(:∅ => []))

#####################################
#Working as expected
plan1 = PLAN1( [Normal(1,2), Gamma(1,2)], [0.9, 0.1] )
plan2 = PLAN2( [Normal(1,2), Gamma(1,2)], [0.9, 0.1],
               Dict(:τ =>[Dirichlet( [1, 2]), Dirichlet( [1, 2])],
                    :μ => [Normal(1,2), Gamma(1,2)] ) )
#PLAN 2 without c field
plan3 = PLAN2( [Normal(1,2), Gamma(1,2)], [0.9, 0.1])

typeof(plan1) #PLAN1
typeof(plan2) #PLAN2
typeof(plan3) #PLAN2 -> as intended

```

One could think of PLAN1 as frequentist model, and PLAN2 as Bayesian model, with the field c as prior for the parameter. I would like that my functions, that I have usually written for the frequentist struct PLAN1, are also working for PLAN2 but ignore the prior fields. This usually works with 90% of my functions except when I need to return the same type of the input as output of some function, like here:

```julia
function fun1(model::PLAN{A}) where A
#some calculations of the fields PLAN.a and PLAN.b
a_new = model.a #simplified
b_new = model.b #simplified
return ( typeof(model)(a_new,b_new) ) ##ERROR MESSAGE HERE
end

fun1(plan1) #WORKING
fun1(plan2 ) #NOT WORKING?!?
fun1(plan3 ) #NOT WORKING?!?

```

**Question1:** I get an error message at the end of fun1 that says ‘no method is defined’ for plan2 and plan3, but I honestly do not know why because I used methods above exactly for the case when the field `c` is missing. I tried to google it and to find it here, but my search was not successful. I guess this might be a common beginner mistake, and would be happy if someone could give me a tip/solution what I did wrong or how I could properly use methods such that fun1 also works for `plan2` without `c` field.

**Question2:** I am interested if it has any performance implications that I did not define the parametric type `C` at the beginning of the struct `PLAN2`, but within the field row. The type itself has not any interesting meaning.

Thank you for your inputs and best regards!

---

<div class="post-metadata">

**Author:** ![JonasIsensee](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonasisensee/32/4704_2.png) [@JonasIsensee](https://discourse.julialang.org/u/JonasIsensee)\
**Post date:** [August 7, 2019, 7:02am UTC](https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218/2 "2019-08-07T07:02:13Z")

</div>

Hey,  
your code has some problem with type stability which I believe causes your observed problems.  
See, your distributions you try to put in this vector have different types.

```julia
typeof(Normal(1,2)) == Normal{Float64}
typeof(Gamma(1,2)) == Gamma{Float64}

```

This means that your vector of distributions cannot be concretely typed.  
If you accept that, then you can simplify the code a little more.

```julia
using Distributions

abstract type PLAN{A<:VariateForm} end

struct PLAN1{A,B} <: PLAN{A}
    a::Vector{ <: Distribution{A}}
    b::Vector{B}
end
struct PLAN2{A,B} <: PLAN{A}
    a::Vector{ <: Distribution{A}}
    b::Vector{B}
    c::Dict{Symbol}
    PLAN2{A,B}(a,b, c= Dict(:∅ => [])) where {A,B}= new(a,b,c)
end
PLAN2(a:: Vector{ <: Distribution{A}},b::Vector{B}, c = Dict(:∅ => [])) where{A,B}= PLAN2{A,B}(a,b,c)

#####################################
#Working as expected
plan1 = PLAN1( [Normal(1,2), Gamma(1,2)], [0.9, 0.1] )
plan2 = PLAN2( [Normal(1,2), Gamma(1,2)], [0.9, 0.1],
               Dict(:τ =>[Dirichlet( [1, 2]), Dirichlet( [1, 2])],
                    :μ => [Normal(1,2), Gamma(1,2)] ) )
#PLAN 2 without c field
plan3 = PLAN2( [Normal(1,2), Gamma(1,2)], [0.9, 0.1])

typeof(plan1) #PLAN1
typeof(plan2) #PLAN2
typeof(plan3) #PLAN2 -> as intended

function fun1(model::PLAN{A}) where A
    #some calculations of the fields PLAN.a and PLAN.b
    a_new = model.a #simplified
    b_new = model.b #simplified
    return ( typeof(model)(a_new,b_new) ) ##ERROR MESSAGE HERE
end

fun1(plan1) #WORKS
fun1(plan2 ) #works
fun1(plan3 ) #works

```

---

<div class="post-metadata">

**Author:** ![jbrea](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jbrea/32/3879_2.png) [@jbrea](https://discourse.julialang.org/u/jbrea)\
**Post date:** [August 7, 2019, 7:13am UTC](https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218/3 "2019-08-07T07:13:07Z")

</div>

1. `typeof(plan2) == PLAN2{Univariate,Float64}` and, indeed, there is no function `PLAN2{Univariate,Float64}(a, b)` but only `PLAN2(a, b)` defined. If you want the type without parameters, you can get it with `typeof(plan2).name.wrapper`, but I would disencourage relying on “internal” fields like `name` and `wrapper`. I don’t know what the best approach would be here, but one possibility is to define functions `initialize(model::PLAN1, a, b) = PLAN1(a, b)` and `initialize(model::PLAN2, a, b) = PLAN2(a, b)`.
2. I suspect that you have a performance decrease. I would benchmark this with `BenchmarkTools`.

By the way, and sorry for raising this when you didn’t ask for it, but I think the outer constructor of `PLAN2` can be simplified, e.g. `PLAN2(a, b, c = Dict(:∅ => [])) = ...` and it is always worth reading a bit in the Julia style guide, in particular [here](https://docs.julialang.org/en/v1/manual/style-guide/#Avoid-writing-overly-specific-types-1).

---

<div class="post-metadata">

**Author:** ![mrVeng](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrveng/32/8836_2.png) [@mrVeng](https://discourse.julialang.org/u/mrVeng)\
**Post date:** [August 7, 2019, 7:23am UTC](https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218/4 "2019-08-07T07:23:06Z")

</div>

Thanks for your reply! That astounds me, I just tested it again and

> fun1(plan2 ) #NOT WORKING?!?  
> fun1(plan3 ) #NOT WORKING?!?

should not work with the current methods.

---

<div class="post-metadata">

**Author:** ![JonasIsensee](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonasisensee/32/4704_2.png) [@JonasIsensee](https://discourse.julialang.org/u/JonasIsensee)\
**Post date:** [August 7, 2019, 7:29am UTC](https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218/5 "2019-08-07T07:29:01Z")

</div>

Yeah, I’m sorry. I messed up.

I updated my previous comment.

---

<div class="post-metadata">

**Author:** ![jbrea](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jbrea/32/3879_2.png) [@jbrea](https://discourse.julialang.org/u/jbrea)\
**Post date:** [August 7, 2019, 7:40am UTC](https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218/6 "2019-08-07T07:40:01Z")

</div>

> [@JonasIsensee](#):
>
> PLAN2{A,B}(a,b, c= Dict(:∅ =\> )) where {A,B}= new(a,b,c)

This is the reason that it works now: you defined `PLAN2{A,B}(a,b)` by making use of the optional argument `c = ...`. The type instabilities are another issue.

---

<div class="post-metadata">

**Author:** ![mrVeng](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrveng/32/8836_2.png) [@mrVeng](https://discourse.julialang.org/u/mrVeng)\
**Post date:** [August 7, 2019, 7:53am UTC](https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218/7 "2019-08-07T07:53:52Z")

</div>

Thank you for your tips!

Regarding 1.:  
It puzzles me that `fun1(plan1)` works nevertheless though, because there is also no ` typeof(plan1) == PLAN1{Univariate,Float64}` defined. I guess it somehow works via ` typeof(plan1)(a,b)`, but I dont seem to find an appropriate method to make it work with the other struct `PLAN2`. That being said, I thought it might work if I add the parametric types as methods like in `PLAN1`:

`PLAN2(a::AbstractVector{<:Distribution{A}}, b::AbstractVector{B}) where {A,B} = PLAN2{A,B}(a, b, Dict(:∅ => []))`,

but without success.

---

<div class="post-metadata">

**Author:** ![jbrea](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jbrea/32/3879_2.png) [@jbrea](https://discourse.julialang.org/u/jbrea)\
**Post date:** [August 7, 2019, 7:57am UTC](https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218/8 "2019-08-07T07:57:36Z")

</div>

> [@mrVeng](#):
>
> It puzzles me that `fun1(plan1)` works nevertheless though, because there is also no `typeof(plan1) == PLAN1{Univariate,Float64}` defined

There is the [default constructor](https://docs.julialang.org/en/v1/manual/constructors/#Inner-Constructor-Methods-1) for `PLAN1`.

---

<div class="post-metadata">

**Author:** ![mrVeng](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrveng/32/8836_2.png) [@mrVeng](https://discourse.julialang.org/u/mrVeng)\
**Post date:** [August 7, 2019, 7:57am UTC](https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218/9 "2019-08-07T07:57:47Z")

</div>

Thank you for your answer! I am aware of the type issue via Distributions but I decided to use that module because it simplifies tasks by a lot.

I am unsure if your suggestion would be a solution because the field `C` should be an optional argument with varying names and length, i.e. different priors for different parameter

---

<div class="post-metadata">

**Author:** ![jbrea](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jbrea/32/3879_2.png) [@jbrea](https://discourse.julialang.org/u/jbrea)\
**Post date:** [August 7, 2019, 8:05am UTC](https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218/10 "2019-08-07T08:05:05Z")

</div>

> [@mrVeng](#):
>
> `PLAN2(a::AbstractVector{<:Distribution{A}}, b::AbstractVector{B}) where {A,B} = PLAN2{A,B}(a, b, Dict(:∅ => []))` ,

You can do `PLAN2{A, B}(a, b) where {A, B} = PLAN2(a, b)` instead.

---

<div class="post-metadata">

**Author:** ![mrVeng](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrveng/32/8836_2.png) [@mrVeng](https://discourse.julialang.org/u/mrVeng)\
**Post date:** [August 7, 2019, 8:30am UTC](https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218/11 "2019-08-07T08:30:04Z")

</div>

Thank you that did the trick - and it works only in conjunction with

`PLAN2(a::AbstractVector{<:Distribution{A}}, b::AbstractVector{B}) where {A,B} = PLAN2{A,B}(a, b, Dict(:∅ => []))`

of course. I will have a look at your style guide and constructors links as well. I also have to benchmark if the parametric type `C` causes any performance issues if it is not stated in the wrapper, but there is not really a solution I could think of to put it there as there is not any concrete type of a union of different distributions.

---

<div class="post-metadata">

**Author:** ![martink](https://avatars.discourse-cdn.com/v4/letter/m/e495f1/32.png) [@martink](https://discourse.julialang.org/u/martink)\
**Post date:** [August 15, 2019, 5:06pm UTC](https://discourse.julialang.org/t/output-the-same-type-of-struct-in-function/27218/12 "2019-08-15T17:06:43Z")

</div>

> [@jbrea](#):
>
> If you want the type without parameters, you can get it with `typeof(plan2).name.wrapper`, but I would disencourage relying on “internal” fields like `name` and `wrapper`.

I ran into a similar problem recently. I wanted to obtain the type of a variable but without any of the parameters, so that i can call the non-parametric constructor and let it figure out the parameters.

I ended up defining a function like this

```julia
typeofNoParam(::PLAN2)=PLAN2
typeofNoParam(::PLAN1)=PLAN1

```

and then use

```julia
typeofNoParam(model)(...)

```

instead of

```julia
typeof(model)(...)

```

It is similar to your solution with the `initialize` function, however, you don’t need to know the number of parameters to the constructor (PLAN1 and PLAN2).

EDIT: I just found an older thread that discussed the same issue with the same solution [Extract type name only from parametric type](https://discourse.julialang.org/t/extract-type-name-only-from-parametric-type/14188)
