As @Tamas_Papp mentioned, abstract types (and types in general)) are primarily useful for dispatch (i.e. so you can get a method that works differently on types
B). Yes, this could be a reasonable way to get what you want. Note, however, that types are not an analogue of classes. They are essentially completely different paradigms.
You could define:
abstract type Processable end
# Should this give an informative error?
# A fallback method? A high level interface?
return Dict(p.items .=> eachindex(p.items))
struct A <: Processable
# Let's say we have some other processable, that
# guarantees unique-ness by construction.
struct UniqueProcessable <: Processable
# This one can do something different from A
# and other generic Processables
# (maybe more efficient) because we know it's
# guaranteed unique!
The above would work and could be a good idea, in case that’s what you intended. Not every problem is best solved in this way though. It makes sense to go this route if multiple
Processables should handle the same method (the same abstract operation) in a different way. For example, arithmetic on
Int64 works differently, but they both are
Real numbers, and we can write generic code for them this way, that we can specialize as needed.
If you’re trying to design an “interface” (rather than sort out dispatch) you can often do so in other ways that may be equally good (sometimes better). For example, instead of the above, you could do:
# this part is generic:
# this part relies on types and dispatch:
reverse_pairs(a::AbstractVector) = a .=> eachindex(a)
reverse_pairs(a::AbstractDict) = values(a) .=> keys(a)
# you can add more methods to `reverse_pairs` to get
# `process` to work like it should.
The above is how generic code must be implemented. For example, plenty of code (in
Base and other places) that works on iterators must work on anything iterable, and yet there is no “
AbstractIterator” (it would be too all-encompassing). Therefore it uses the “iterator interface”, functions like
length/size, etc. to accomplish this.
Note that in the second example,
reverse_pairs depends on types and dispatch to work in each case, but
process itself is generic.
There’s a lot more to be said on the topic, really, but I think the best thing is just to continue increasing familiarity with the language. All of this isn’t unique to julia, but manifests itself a bit idiosyncratically.