Incomplete initialization (control which fields are initialized in `new`)

zero does’t work for this reason.


That is correct. I have checks in place to guarantee that I won’t access undefined variables. The same you would do in C, if you like embracing the thrill of undefined behavior as much as I do :smiley:


I specifically want all my bounded/unbounded/open/closed intervals to be of the same type Interval{T}, capturing only the nature of the underlying total order T we are working in, and not the “kind” of the interval. This is different from what is done in IntervalSets with

struct Interval{L,R,T}  <: TypedEndpointsInterval{L,R,T}
    left::T
    right::T

    Interval{L,R,T}(l, r) where {L,R,T} = ((a, b) = checked_conversion(T, l, r); new{L,R,T}(a, b))
end

The reason is that I have to work with collections of intervals of different kinds, and storing them in a heterogeneous array Vector{Interval{L,R,Int} where {L,R} incurs a performance overhead (~10x), which I want to get rid of.


The only other possibility that comes to my mind is to always store the endpoint in left for both [x,∞) and (-∞,x] (which among left_kind and right_kind is :unbounded disambiguates between the two). But that is just nasty and very error prone in the rest of the code. Everything would be so simple if I could just initialize only right and not left.

What do I mean with this last approach?

This is what I mean by “always store in left”:

    function Interval{T}(
        left_kind::Symbol,
        right_kind::Symbol,
        value::T,
        checks::Val{Checks} = Val(true),
    )::Interval{T} where {T,Checks}
        if Checks
            left_kind == :closed ||
                left_kind == :open ||
                left_kind == :unbounded ||
                error("invalid left endpoint kind")
            right_kind == :closed ||
                right_kind == :open ||
                right_kind == :unbounded ||
                error("invalid right endpoint kind")
            (left_kind == :unbounded && right_kind != :unbounded) ||
                (left_kind != :unbounded && right_kind == :unbounded) ||
                error("exactly one endpoint must be unbounded")
        end
        new{T}(left_kind, right_kind, value)
    end

Exactly one endpoint has to be :unbounded, but the value of the other endpoint is always stored in left, while right is left uninitialized. This however complicates further code, for instance the one retrieving a left/right endpoint if it exists:

function left_endpoint(int::Interval{T})::Union{T,Nothing} where {T}
    if int.left_kind == :unbounded
        nothing
    else
        int.left
    end
end

function right_endpoint(int::Interval{T})::Union{T,Nothing} where {T}
    if int.right_kind == :unbounded
        nothing
    else
        if int.left_kind == :unbounded
            int.left
        else
            int.right
        end
    end
end