Is it possible to preserve the fields and methods of a type when creating a subtype?

Hi suppose I have a SubDataFrame A with columns A.Roll, A.Pitch, A.Yaw. Further suppose I create a new type in order to apply specific methods.

struct Plane
    flight :: SubDataFrame 
end 

B =  Plane(A)

Is it possible for type Plane to inherit the fields of a SubDataFrame? So for example ‘A.Roll’ would return the column Roll. B.Roll however returns ERROR: type plane has no field Roll.
Likewise A.Time = [1,2,3] would add a new column Time to A whereas it does not work for type Plane. I realize in the first instance I could do something like

Roll(Plane) = Plane.flight.Roll 

But I was just wondering if there was a way to get Plane <: SubDataFrame so that it can use the same fields and methods? Thanks and sorry if this is another newbie question.

You can overload getproperty and setproperty!:

mutable struct SubDataFrame
    Roll
    Pitch
    Yaw
end

struct Plane
    flight :: SubDataFrame
end

A = SubDataFrame(1,2,3)
B = Plane(A)

get_flight(B :: Plane) = getfield(B, :flight);
Base.getproperty(B :: Plane, n :: Symbol) = getproperty(get_flight(B), n)
Base.setproperty!(B :: Plane, n :: Symbol, x) = setproperty!(get_flight(B), n, x)

A.Roll === B.Roll #true
A.Roll = 17
B.Roll  # 17
1 Like

Thanks so much, this works great! Sorry for the delayed response. Would you mind clarifying how Base.getproperty and Base.setpropperty! are working? After a bunch of googling and reading the documentation in Julia I’m still not sure I have it right.

My understanding is that with:
Base.getproperty(B :: Plane, n :: Symbol) = getproperty(get_flight(B), n)
we are setting B.n === A.n or Plane.n ===SubDataFrame.n

likewise:
Base.setproperty!(B :: Plane, n :: Symbol, x) = setproperty!(get_flight(B), n, x)
allowing us to create a new column x in B via B.n = x just as we would with SubDataFrame. Is this correct?

Also why is it that the Base module is required on one side of the assignment but not the other? Sorry just trying to understand this a little better. Thanks.

Base.getproperty is what is called when you write B.Roll.
The overloading makes that effectively into B.flight.Roll.
We need the get_flight because
getproperty(B.flight, :Roll
expands to
getproperty(getproperty(B, :flight), :Roll)
which results in a StackOverflow (infinite recursion).

To avoid that, we need to use getfield(B, :flight) to retrieve the flight field.
Internally, getproperty defaults to getfield unless we overload it.

Basically the same happens with setproperty!.
So the expected syntax B.Roll for retrieval and B.Roll = 17 for assignment work.

1 Like

awesome thanks so much for clearing that up!