If the type hierarchy is implemented well, functions for `Dict` would
ideally be written to work with `super(Dict)` (i.e., `Associative`).  And
in fact, some are--e.g., `in`, `haskey`, `show`, `keys`, `values`, etc.
See `base/dict.jl` for the implementation.

Now, it could be argued that the type hierarchy isn't complete enough, and
that there should be an additional abstract type above `Dict` (maybe
`AbstractDict`), for the cases that `Associative` isn't specific enough to
`Dict` extensions.

To work around that, the implementation of `DefaultDict` (and other
dictionary types) in `DataStructures.jl` wraps a `Dict` (as in your
implementation), and uses a `@delegate` macro to delegate function calls to
the wrapped `Dict`.  This has been copied a few other places, but it's hard
to say this is the best or most Julian way to do this.

(In fact, I just noticed an error that I thought was fixed: `empty!()` is
delegated to the wrapped dictionary, but in fact should return the empty
outer dictionary, not the internal one.)

(This was discussed previously here
<https://groups.google.com/d/topic/julia-dev/MV7lYRgAcB0/discussion> and
here <https://groups.google.com/d/topic/julia-users/Wwn3KHmmm9I/discussion>
.)

Hope this helps.

Cheers,
   Kevin



On Sun, Aug 23, 2015 at 8:11 AM, Joshua Ballanco <[email protected]> wrote:

> Hello all,
>
> Apologies in advance if I’m missing something obvious. I’ve only just
> started experimenting with some of the more advanced features of Julia’s
> types, and I’ve hit a bit of a wall…
>
> Say I wanted to replicate something like Python’s
> `collections.defaultdict` or Ruby’s `Hash` class with a default value. So
> far this is what I have:
>
> ——8<——8<——
>
>     immutable DefaultDict{K, V} <: Associative{K, V}
>       default::V
>       dict::Dict{K, V}
>     end
>
>     function DefaultDict(val)
>       return DefaultDict(val, Dict())
>     end
>
>     function Base.getindex{K, V}(dd::DefaultDict{K, V}, key::K)
>       if haskey(dd.dict, key)
>         getindex(dd.dict, key)
>       else
>         dd.default
>       end
>     end
>
> ——8<——8<——
>
> Ideally, I’d like to avoid having to re-implement all of the methods one
> would expect `Dict` to work with, since I can trivially convert from a
> `DefaultDict`:
>
> ——8<——8<——
>
>     Base.convert(::Type{Associative}, dd::DefaultDict) = dd.dict
>     Base.promote_rule(::Type{Associative}, ::Type{DefaultDict}) =
> Associative
>
> ——8<——8<——
>
> However, this doesn’t seem to work as I had hoped it might:
>
> ——8<——8<——
>
>     > foo = DefaultDict(42)
>     > foo['a'] = 1
>     ERROR: MethodError: `setindex!` has no method matching
> setindex!(::DefaultDict{Any,Any}, ::Int64, ::Char)
>     Closest candidates are:
>       setindex!(::Associative{K,V}, ::Any, ::Any, ::Any, ::Any…)
>
> ——8<——8<——
>
> Sure, implementing `setindex!`, `length`, `start`, etc. would be trivial,
> but I’m more worried about third-party code that expects `Dict` arguments.
> In principle I’d like to still be able to pass instances of `DefaultDict`
> to methods that would otherwise expect `Dict`s.
>
> Am I completely off-base with this approach? What’s the most “Julian”
> solution to specializing types in this manner?
>
> Cheers,
>
> Josh
>

Reply via email to