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 >
