RecursiveDicts
A RecursiveDict is a wrapped Dict, where values may always be a Dict (or RecursiveDict) with the same type parameters.
Examples
It's a drop-in replacement for a standard Dict:
julia> rdict = RecursiveDict(:a => 1, :b =>2)
RecursiveDict{Symbol, Int64} with 2 entries:
:a => 1
:b => 2
julia> rdict[:c] = Dict(:d => 5, :e => 20)
Dict{Symbol, Int64} with 2 entries:
:d => 5
:e => 20
julia> rdict
RecursiveDict{Symbol, Int64} with 3 entries:
:a => 1
:b => 2
:c => RecursiveDict{Symbol, Int64}(:d=>5, :e=>20)
julia> rdict[:f] = Dict("str" => 7)
ERROR: MethodError: Cannot `convert` an object of type
RecursiveDict{String, Int64} to an object of type
Union{Int64, RecursiveDict{Symbol, Int64}}
[...]Why Does the Type Signature Look Like That?
Type parameters express the free variables in the type. In a Dict{K,V}, the keytype and valtype are both free, so K and V are both the keytype and valtype, and the free parameters.
julia> d = Dict{Int,String}(1 => "one", 2 => "two")
Dict{Int64, String} with 2 entries:
2 => "two"
1 => "one"
julia> typeof(d)
Dict{Int64, String}
julia> keytype(d)
Int64
julia> valtype(d)
StringWith a RecursiveDict, the free parameters are the keytype, and whatever value type other than that sort of RecursiveDict is allowed, so the above identity doesn't hold.
julia> rd = RecursiveDict{Int,String}(1 => "one", 2 => "two")
RecursiveDict{Int64, String} with 2 entries:
2 => "two"
1 => "one"
julia> typeof(rd)
RecursiveDict{Int64, String}
julia> keytype(rd)
Int64
julia> valtype(rd)
Union{String, RecursiveDict{Int64, String}}This fact is why RecursiveDicts exists, since it makes it impossible to specify a recursive container without wrapping it in a struct.
Docstring
RecursiveDicts.RecursiveDict — TypeRecursiveDict{K,V} <: AbstractDict{K,Union{V,RecursiveDict}}A Dict where an instance of itself is always a valid value. So a RecursiveDict{String,String} has String keys, and the values may be either Strings or a RecursiveDict{String,String}. It's harmless to write this as RecursiveDict{String,Union{String,RecursiveDict{String,String}}}, although it isn't necessary, and as the signature gestures at, one does have to provide the base case eventually.
Implements all Base methods which take a Dict. convert will return the wrapped Dict which provides the data structure, as a shared reference, meaning changes to the provided Dict will be seen in the RecursiveDict, so this data structure may be used anywhere which expects a Dict, with a bit of care.
A Dict{K,V} may also be converted to a RecursiveDict{K,V}, without copying.