# Outer constructor not getting called despite being in stack trace?

**URL:** <https://discourse.julialang.org/t/outer-constructor-not-getting-called-despite-being-in-stack-trace/103801>\
**Category:** New to Julia\
**Tags:** constructors\
**Created:** [September 13, 2023, 4:52am UTC](https://discourse.julialang.org/t/outer-constructor-not-getting-called-despite-being-in-stack-trace/103801 "2023-09-13T04:52:40Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![deaminase](https://avatars.discourse-cdn.com/v4/letter/d/b2d939/32.png) [@deaminase](https://discourse.julialang.org/u/deaminase)\
**Post date:** [September 13, 2023, 4:52am UTC](https://discourse.julialang.org/t/outer-constructor-not-getting-called-despite-being-in-stack-trace/103801/1 "2023-09-13T04:52:40Z")

</div>

Somehow Julia’s constructors manage to consistently confuse me. Here is a pared-down example of what I’m trying and failing to do:

```julia
const Optional{T} = Union{Some{T}, Nothing}

struct State{InpIterState,Input}
  full_input::Input
  in_iter_state::Optional{InpIterState}
end

function State(input, in_iter_state=nothing)
  let II = Base.notnothing(in_iter_state) ? typeof(something(in_iter_state)) : iter_state_type_for(input)
    State{II,typeof(input)}(input, in_iter_state)
  end
end

iter_state_type_for(::Array) = Int
iter_state_type_for(x) = error("🤷")

```

But calling `State([1,2])` dies a fiery death because it actually ends up calling a type parameterless inner constructor and `InpIterState` can’t be inferred from a `Nothing` (I guess the default method argument handling causes `State([1,2])` to end up calling the inner constructor?). I was a bit surprised that the outer constructor shows up on the stack trace even though it isn’t actually getting called (I tried replacing the body with just an `error("err")` and it had no effect):

```julia
UndefVarError: `InpIterState` not defined

Stacktrace:
 [1] State(full_input::Vector{Int64}, in_iter_state::Nothing)
   @ Main scratch.ipynb:4
 [2] State(input::Vector{Int64})
   @ Main scratch.ipynb:9
 [3] top-level scope
   @ scratch.ipynb:17

```

Now, two questions:

1. why does the stack trace say the outer constructor was called even though it seemingly wasn’t?
2. how _should_ I be doing this? I want to provide an outer constructor that doesn’t take any type parameters and figures out the correct `InpIterState` type parameter, and with a default `in_iter_state=nothing`.

**Edit:** I think I figured out why the inner constructor’s getting called instead of the outer one:

```julia
# 3 methods for type constructor:
State(input) in Main at scratch.ipynb:8
State(full_input::Input, in_iter_state::Union{Nothing, Some{InpIterState}}) where {InpIterState, Input} in Main at scratch.ipynb:4
State(input, in_iter_state) in Main at scratch.ipynb:8

```

If my understanding is correct the inner constructor is getting called because out of those options it has the most specific matching type? So how do I make it not do that? 😁

---

<div class="post-metadata">

**Author:** ![Sevi](https://avatars.discourse-cdn.com/v4/letter/s/c67d28/32.png) [@Sevi](https://discourse.julialang.org/u/Sevi)\
**Post date:** [September 13, 2023, 5:52am UTC](https://discourse.julialang.org/t/outer-constructor-not-getting-called-despite-being-in-stack-trace/103801/2 "2023-09-13T05:52:21Z")

</div>

> [@deaminase](#):
>
> If my understanding is correct the inner constructor is getting called because out of those options it has the most specific matching type? So how do I make it not do that? 😁

Yes, the most specific method is called. Why not just make your outer constructor into an inner constructor instead?

If you don’t want to overwrite it, these should also work:

- make the outer constructor use keyword arguments `State(; input, state=nothing)`
- define a different function `make_state(input, state=nothing)` that looks exactly like your outer constructor now (just don’t name it `State`)
- specify the argument type in the outer constructor: `State(input, in_iter_state::Optional{T}=nothing) where T`. Then it should have the same signature and be called instead of the inner constructor.

---

<div class="post-metadata">

**Author:** ![deaminase](https://avatars.discourse-cdn.com/v4/letter/d/b2d939/32.png) [@deaminase](https://discourse.julialang.org/u/deaminase)\
**Post date:** [September 13, 2023, 6:23am UTC](https://discourse.julialang.org/t/outer-constructor-not-getting-called-despite-being-in-stack-trace/103801/3 "2023-09-13T06:23:29Z")

</div>

Ohhh I didn’t even think to just provide the parameter type in the outer constructor as well. What I ended up doing was just defining an inner constructor with all the type parameters present, but it feels a bit redundant since it doesn’t really “do” anything like check argument validity etc.

I didn’t want to make the parameterless constructor an inner constructor since I’ll also want to provide at least a `State{InpIterState}(inp,is::Optional{InpIterState})` as well, and I’ve understood that generally you’d only want one inner constructor. Then again, I seem to follow best practices pretty selectively since apparently using constructors to provide default type parameters like I’ve been doing and was planning to do with `State{InpIterState}(inp,is)` is somewhat inadvisable as well due to some potential ambiguities (see [this comment](https://discourse.julialang.org/t/type-system-abuse-advisability-of/103390/9))

---

<div class="post-metadata">

**Author:** ![Sevi](https://avatars.discourse-cdn.com/v4/letter/s/c67d28/32.png) [@Sevi](https://discourse.julialang.org/u/Sevi)\
**Post date:** [September 13, 2023, 6:42am UTC](https://discourse.julialang.org/t/outer-constructor-not-getting-called-despite-being-in-stack-trace/103801/4 "2023-09-13T06:42:50Z")

</div>

Sure! There isn’t always a single “right” way to do it I think. Personally, I tend to define inner constructors only when checking the argument values for something, like you mentioned. Using outer constructors and `make_...` functions leaves a bit more flexibility if I realize I have to change something later (I can just add new ways of constructing the object vs. I have to change the inner constructor and perhaps break/update a lot of code).

If you want to provide default type parameters, would a type alias help? Analogously to (roughly) `Vector{Int} = Array{Int, 1}`.
