3

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?

3
  • Putting aside question of any benefit you get from inheriting from a parameterized AbstractDict rather than just an AbstractDict --- you are finding that Julia will try to collapse a Union{ConcreteType, Union{}} into a Concrete type. Try this in REPL: julia> Union{String, Number}, Union{String, Union{}}, Union{String, Any} ===> (Union{Number, String}, String, Any). That is where your Dict{String, String} is from. I would suggest you drop the struct and use a Dict{String, Any} instead, and switch to arrays instead of Dicts altogether if performance turns into an issue with Any. Commented Sep 13 at 21:28
  • "parameter seems to be ignored". Yes, the D==true expression is always false. Wrapping each D==true as println_and_return(D)==true shows that when the struct is evaluated, D is Any. When Test{true}(Dict{String, Union{String, Number}}()) is called, D is not evaluated. So the condition expressions in the struct supertype and field type expressions are not evaluated for the constructor expression, and therefore cannot access the constructor type parameter value. Commented Sep 14 at 12:23
  • What is the purpose of this over just using two separate types, or one type that explicitly specifies the type of the dict values? Commented Sep 14 at 15:39

1 Answer 1

4

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:

  • Add struct type parameter V to specify that the value types are the same in supertype and field.
  • Specify each boolean value in an inner constructor type parameter to constrain its result type.
  • Omit the V in each inner constructor type parameter, and specify its value in each new expression.
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
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.