I have the following type hierarchy:
abstract type U end
struct A <: U ... end
struct B <: U ... end
(I’ve omitted the details, but
B both have identical field-names and types, but the semantic meaning of their fields is quite different, hence using two structs instead of one.) I also have a conversion function from
I have written several functions that apply only to data of type
B such as :
+(b::B, x::Number)= ...
*(b::B, x::Number)= ...
+(b::B, b::B)= ...
I want the ability to use these functions when the arguments of type
B are replaced with arguments of type
A. What is the most concise way of doing this? I see the following options:
- For each function above, manually write a method for type
A arguments using
convert as in…
+(a::A, x::Number)= +(a2b(a), x)
- Use a macro to accomplish #1. I’m just cutting my teeth here on Julia, so I’d rather avoid this option if a better one exists.
- Use the type system in a different way that I’m doing here?
I’m hoping (and suspecting) #3 is the correct answer, I just don’t see how to do it.
Please help!! And thanks!
I would suggest something like:
+(u::U, x::Number) = in terms of some set of methods that define whatever API you expect
B to implement
- Implement those methods for both
- That’s it
This has a bunch of advantages:
- It removes the assumption that A and B must have identical fields or be perfectly convertible from one to the other. That’s true now, but will it always hold?
- It makes it more clear what the new type
C <: U should do when you inevitably decide you need it
This is a super common pattern in Julia. One example is
AbstractArray: There are lots of methods which work on any kind of
AbstractArray because they use the common methods like
getindex(). It’s easy to implement a new kind of
AbstractArray by just defining
getindex() for your new type, and then all the existing functions of
AbstractArray should just work.
Your proposal to use
a2b internally would be like forcing all array-like functions to first convert their inputs to a dense
Matrix before operating on them. That would be unnecessarily slow at best and impossible at worst. Using a set of common methods, instead, and requiring users to implement those methods for their new types, makes things much easier to manage.
Awesome. Thank you; exactly what I needed!