Bijections for Mutable Structures
Mutating keys/values can lead to corruption
The safest use of a Bijection
is when the keys and values are immutable. If a mutable key or value in a Bijection
is altered, the bijective property can be compromised. Here is an example:
julia> b = Bijection{Int, Vector{Int}}();
julia> b[1] = [1,2,3]
3-element Vector{Int64}:
1
2
3
julia> b[2] = [1,2,4]
3-element Vector{Int64}:
1
2
4
julia> b[2][3] = 3
3
julia> b
Bijection{Int64, Vector{Int64}, Dict{Int64, Vector{Int64}}, Dict{Vector{Int64}, Int64}} with 2 entries:
2 => [1, 2, 3]
1 => [1, 2, 3]
Notice that b
contains a repeated value and therefore is not bijective.
Some strategies to avoid this problem include:
- Only use immutable keys and values (such as numbers and strings).
- Use copies of the keys/values in the
Bijection
. - Don't modify keys/values saved in the
Bijection
.
In case none of these is a viable option, we provide the following additional alternative.
Keys/values as objects
The issue in the example presented above is that distinct Julia objects may be equal, but not the same object. For example:
julia> v = [1,2,3];
julia> w = [1,2,3];
julia> v==w
true
julia> v===w
false
We may wish to create a Bijection
in which the keys or values are permitted to be equal, but are distinct objects. Julia's IdDict
is a variation of Dict
in which keys/values are considered different if they are distinct object (even if they hold the same data). To replicate this behavior in a Bijection
use this longer form constructor:
Bijection{K, V, IdDict{K,V}, IdDict{V,K}}()
where K
is the type of the keys and V
is the type of the values.
For example:
julia> b = Bijection{Vector{Int}, String, IdDict{Vector{Int},String}, IdDict{String,Vector{Int}}}();
julia> b[ [1,2,3] ] = "alpha";
julia> b[ [1,2,3] ] = "beta";
julia> b("alpha") == b("beta")
true
julia> b("alpha") === b("beta")
false
julia> keys(b)
KeySet for a IdDict{Vector{Int64}, String} with 2 entries. Keys:
[1, 2, 3]
[1, 2, 3]
julia> Set(keys(b))
Set{Vector{Int64}} with 1 element:
[1, 2, 3]