Somehow Julia’s constructors manage to consistently confuse me. Here is a pared-down example of what I’m trying and failing to do:
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):
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:
- why does the stack trace say the outer constructor was called even though it seemingly wasn’t?
- 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 defaultin_iter_state=nothing
.
Edit: I think I figured out why the inner constructor’s getting called instead of the outer one:
# 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?