Changing struct fields to a different sub-type during run time

That’s what I remembered from another thread. But now that I’m actually trying the package and looking into the source code, it’s type-stable and scales up if every type has seconds::Float64. For a more modest example that exceeds the automatic Union-splitting optimization of 3, getproperty does something like this:

        v = (LightSumTypes).unwrap(sumt)
        if v isa T1
            return (Base).getproperty(v, s)
        elseif v isa T2
            return (Base).getproperty(v, s)
        elseif v isa T3
            return (Base).getproperty(v, s)
        elseif v isa T4
            return (Base).getproperty(v, s)
        elseif v isa T5
            return (Base).getproperty(v, s)
        else
            error("THIS_SHOULD_BE_UNREACHABLE")
        end

So while a normal getproperty call or equivalent structure.property expression for a type-unstable structure::Union{...} would fail, the concrete LightSumType would branch to every specified type and separately call getproperty again. If all those inner getpropertys have the same concrete return type, then the overall getproperty shares the type; if they don’t, then it takes very little variation to lose type stability. Pretty much every fundamental function generated by @sumtype has this branch, and it doesn’t have the extra help of assertions or conversions. The successor (in the sense that both are authored by Tortar) WrappedUnions.@unionsplit puts that branch generation in the user’s hands, which I am guessing would also let the user add convert calls to restore type stability. To manually write the branch’s expressions instead of generating duplicates, you’d need an API like @cases of SumTypes.jl; I mention this because I expect the various time types to typically have different operations, not just Float64 arithmetic.

f surprised me a bit. variant evidently does not reach a stable return type via some large branch; that branch can’t happen at that stage anyway. However, the compiler was still able to spot the 50 f methods for the 50 T#=i=# types all reached seconds::Float64; to my knowledge, that far exceeds the default Union-splitting optimization (3) and inference of multiple applicable methods (4). I don’t understand the compiler much, maybe someone else can comment. EDIT: The limit was removed for linear signatures, whatever that means RFC: inference: remove union-split limit for linear signatures by vtjnash · Pull Request #37378 · JuliaLang/julia · GitHub

A big set of types has other limits however. The Union-annotated field of these approaches are internally implemented like C’s tagged unions if all the types are isbits, but that only goes up to 256 types because the tag is 1 byte. Even if that limit is increased, the parser currently hits a recursion limit for if-elseif branches somewhere between 256 and 65536.