Way to construct multi-key ImmutableDict

question

#1

if you try to do,

julia> Base.ImmutableDict(
         :a => 1,
         :b => 2
       )

you get,

ERROR: MethodError: no method matching Base.ImmutableDict(::Pair{Symbol,Int64}, ::Pair{Symbol,Int64})
Closest candidates are:
  Base.ImmutableDict(::Pair{K,V}) where {K, V} at dict.jl:624
  Base.ImmutableDict(::Base.ImmutableDict{K,V}, ::Pair) where {K, V} at dict.jl:625

how do you construct a multi-key immutable dictionary?


#2

with the (undocumented!) constructor

ImmutableDict{K,V}(parent::ImmutableDict, key, value) where {K,V} = new(parent, key, value)

or more typically via the convenience version:

    d = ImmutableDict(:a => 1)
    d1 = ImmutableDict(d, :b => 2)

(I learnt this from the test suite.)


#3

Is there a way to combine this with:


#4

In dict.jl, this:

# this actually defines reverse iteration (e.g. it should not be used for merge/copy/filter type operations)
start(t::ImmutableDict) = t
next(::ImmutableDict{K,V}, t) where {K,V} = (Pair{K,V}(t.key, t.value), t.parent)
done(::ImmutableDict, t) = !isdefined(t, :parent)

is not very encouraging for merge!(d1::ImmutableDict,d2::ImmutableDict)


#5

Defining this method is useful for populating an ImmutableDict from single Dict.

function Base.merge!(d::Base.ImmutableDict, dsrc::Dict)
    for (k,v) in dsrc
        d = Base.ImmutableDict(d,k=>v)
    end
    return d
end
julia> dsrc = Dict(1=>2, 3=>4);

julia> d = merge!(Base.ImmutableDict{Int,Int}(), dsrc)
Base.ImmutableDict{Int64,Int64} with 2 entries:
  1 => 2
  3 => 4

#6

ImmutableDict is immutable, thus strictly speaking merge! doesn’t apply to it.

The answer to your original question was also here (found just by looking up “ImmutableDict” here, there are just 4 results): Initialising a dictionary with duplicate keys


#7

Yes, you are right. If you do merge!(d1,d2), then d1 will still be bound to the same unchanged object. So the name merge! is incorrect.

The method in the post you link is a solution for the (minimal) example given in the original question— but only for exactly two entries. In any case, I think that either the post you linked or my answer give enough information for @djsegal to construct something useful.


#8

This is true, but a Dict usually has more than one or two entries?

Can someone explain why ImmutableDict doesn’t allow construction with multiple pairs to begin with?

I just thought the merge! trick would allow you to do this under the hood.

// not really trying to make ImmutableDicts mutable


#9

I’m not sure what are you referring to.

Strictly speaking there is no such thing like an ImmutableDict with multiple keys, they’re nested dictionaries. What you ask for is a convenience to build such object. Perhaps there aren’t many people using this type? (I do.)


#10

Isn’t an ImmutableDict the immutable counterpart of a Dict?

Dicts usually have multiple keys (or you would just make them into a variable)?


By example,

Base.ImmutableDict(
    :a => 1,
    :b => 42,
    :c => 404
 )

is how I would usually use a Dict. Not just:

Base.ImmutableDict(
    :a => 1
 )

In this case, I would just use a variable:

a = 1

#11

Isn’t an ImmutableDict the immutable counterpart of a Dict?

Yes. From the documentation:

ImmutableDict is a Dictionary implemented as an immutable linked list.

The implementation is a particular singly-linked list. Each node has three fields, a key, a value, and a link to the parent node. The interface is that of a Dict, with some restrictions, partly due to the implementation as a linked list.

An immutable dict can certainly hold an arbitrary number of keys/values. Each node, of type ImmutableDict holds only one key/value pair. But, there is no container of these nodes that makes Dict; they are just linked together.

The interface does not allow construction with more than one key/value pair. But, a method to do this would be straightforward. My guess is that it was not needed for the task that this particular implementation was written for.


#12

It sounds more appropriate to call this an ImmutablePair then?

And the parent list might be thought of as the ImmutableDict?

But maybe my point/use-case doesn’t make any sense?


#13

It sounds more appropriate to call this an ImmutablePair then?

Yes, I see what you mean. But this kinda ruins the illusion that you have a Dict.

And the parent list might be thought of as the ImmutableDict?

There is no parent list. Each node, including the parent node, is an instance of ImmuatableDict.

I’m not sure what your use case is. But, it is possible to construct an ImmuatbleDict from a list of pairs, and then to retrieve them. It’s just that the method is not written.


#14
function Base.ImmutableDict(KV::Pair{K,V}, KVs::Pair{K,V}...) where {K,V}
    d = Base.ImmutableDict(KV)
    for p in KVs
        d = Base.ImmutableDict(d,p)
    end
    return d
end
julia> Base.ImmutableDict(
           :a => 1,
           :b => 42,
           :c => 404
        )

ImmutableDict{Symbol,Int64} with 3 entries:
  :c => 404
  :b => 42
  :a => 1
`