This is an interesting pattern which leads to very efficient code. However, the readability is not so great.
What I use in these cases:
- Always introduce an abstract type per concrete type, at least for types which are public. For
MyTypethis would beAMyType(because it is aMyTypein the sense of “I don’t care which one” – interestingly, this implies the same meaning asAbstractMyTypeorAnyMyType, but is shorter and simpler to read after getting used to) - Use the concrete type in the field constraints of your structs (obviously).
- Never type-constraint your methods to the concrete type, but to the abstract type.
- If you want to extend a type, it’s then simply introducing another abstract type (to again be reusable) and letting a macro from one of the packages copy over the fields into a new type.
No downsides of this procedure have been identified when I asked here.
One day I should probably create a package for this so that you could e.g. write
@extendable struct Base
baseField::Int
end
function do_something(base::ABase)
base.baseField
end
and it would just work. Or even better
using ExtendableModules
@extendable module MyModule
struct Base
baseField::Int
end
function do_something(base::Base)
base.baseField
end
end
where the macro would rewrite all structs in the module to define their abstract type, too. Additionally, it would rewrite all method type constraints to use the abstract types. That would be nice.