julia

Parametric struct with type of member depending on value type of struct


I'm trying to write a parametric struct where one of the parameters is a Boolean value type, and where the type of a member should depend on the value of this Boolean. The struct should also inherit from a parametric type, where the parameters again depend on the Boolean. Simplified, this looks something like this:

struct Test{D} <: AbstractDict{String, Union{String, D==true ? Number : Union{}}}
    d::Dict{String, Union{String, D==true ? Number : Union{}}}
end

However, when I create an object with Test{true}(Dict{String, Union{String, Number}}()) the type parameter seems to always be ignored and I end up with d::Dict{String, String}.

Is what I'm trying to do even possible in Julia?


Solution

  • Apparently, one way to specify a type V, for both field type and supertype parameters, that depends on a constructor boolean value type parameter D, without specifying V in the constructor type parameter, is to:

    struct TestC{D,V} <: AbstractDict{String, V}
        d::AbstractDict{String, V}
        TestC{true}() = new{true, Union{String, Number}}(Dict{String, Union{String, Number}}())
        TestC{false}() = new{false, String}(Dict{String, String}())
        TestC(aDict::S) where S <: AbstractDict{String, Union{String, Number}} = new{true, Union{String, Number}}(aDict)
        TestC(aDict::S) where S <: AbstractDict{String, String} = new{false, String}(aDict)
    end
    
    import Base: iterate, length, get
    Base.iterate(t::TestC) = iterate(t.d)
    Base.iterate(t::TestC, i::Int64) = iterate(t.d, i)
    Base.length(t::TestC) = length(t.d)
    Base.get(t::TestC, k::String, dflt) = get(t.d, k, dflt)
    
    import Test: @testset, @test
    @testset begin
        @test TestC{true, Union{String, Number}} == typeof(TestC{true}())
        @test TestC{false, String} == typeof(TestC{false}())
        @test TestC{true, Union{String, Number}} == typeof(TestC(Dict{String, Union{String, Number}}()))
        @test TestC{false, String} == typeof(TestC(Dict{String, String}()))
        @test "TestC{true, Union{Number, String}}(\"one\" => 1)" == string(TestC(Dict{String, Union{String, Number}}("one" => 1)))
        @test 1 == TestC(Dict{String, Union{String, Number}}("one" => 1))["one"]
    
        @test TestC{true, Union{String, Number}} <: AbstractDict{String, Union{String, Number}}
        @test TestC{false, String} <: AbstractDict{String, String}
        @test false == (TestC{true, Union{String, Number}} <: AbstractDict{String, String})
        @test false == (TestC{false, String} <: AbstractDict{String, Union{String, Number}})
    
        @test "TestC{true, Union{Number, String}}(\"one\" => 1)" == string(TestC(TestC(Dict{String, Union{String, Number}}("one" => 1))))
        @test "TestC{false, String}()" == string(TestC(TestC{false}()))
    end