Currently, convert() is described as automatically implementing downcasts to a descendant or upcasts to an ancestor class.* However, I was surprised to discover that it also attempts "sibling" casts; that is, casts to a class that is neither an ancestor nor a descendant, but which has a shared ancestor. Is this intended behavior?
Consider these classes, where Base has children A and B, and B has child B_child:
Base = new_class("Base")
A = new_class(
"A",
parent = Base,
properties = list(x = new_property(class_numeric, default = 1))
)
B = new_class("B", parent = Base)
B_child = new_class(
"B_child",
parent = B,
properties = list(x = new_property(class_character, default = "hello"))
)
Per the documentation, I'd expect the following to work (and they do):
convert(B_child(), Base)
## <Base>
convert(B(), B_child)
## <B_child>
## @ x: chr "hello"
Surprisingly (to me), I can also automatically convert to a sibling; e.g. B to A even though neither is a subclass of the other:
convert(B(), A)
## <A>
## @ x: num 1
It will also attempt to convert B_child to A, though it will fail because their properties are not compatible:
convert(B_child(), A)
## Error: <A> object properties are invalid:
## - @x must be <integer> or <double>, not <character>
Personally, I would consider either of these sibling casts to be an error, as it is not clear to me that one should expect sibling classes to share semantics under which an automatic conversion is likely to make sense. If one ever did want to do a sibling cast, it would be easy enough to do explicitly by going through the base class, a la convert(convert(B_child(), Base), A).
I believe this is happening because convert is checking inheritance via is_parent_instance() here:
|
} else if (is_parent_instance(from, to)) { |
Which reports siblings as parents:
S7:::is_parent_instance(B(), A)
## [1] TRUE
*Sidebar: I believe the documentation (and comments in source) of convert() currently uses "upcast" and "downcast" incorrectly: wherever I've encountered these, "upcast" indicates a cast going up the class tree (to an ancestor) and "downcast" indicates a cast going down the class tree (to a descendent); see e.g. this wiki article.
Currently,
convert()is described as automatically implementing downcasts to a descendant or upcasts to an ancestor class.* However, I was surprised to discover that it also attempts "sibling" casts; that is, casts to a class that is neither an ancestor nor a descendant, but which has a shared ancestor. Is this intended behavior?Consider these classes, where
Basehas childrenAandB, andBhas childB_child:Per the documentation, I'd expect the following to work (and they do):
Surprisingly (to me), I can also automatically convert to a sibling; e.g.
BtoAeven though neither is a subclass of the other:It will also attempt to convert
B_childtoA, though it will fail because their properties are not compatible:Personally, I would consider either of these sibling casts to be an error, as it is not clear to me that one should expect sibling classes to share semantics under which an automatic conversion is likely to make sense. If one ever did want to do a sibling cast, it would be easy enough to do explicitly by going through the base class, a la
convert(convert(B_child(), Base), A).I believe this is happening because
convertis checking inheritance viais_parent_instance()here:S7/R/convert.R
Line 106 in db59642
Which reports siblings as parents:
*Sidebar: I believe the documentation (and comments in source) of
convert()currently uses "upcast" and "downcast" incorrectly: wherever I've encountered these, "upcast" indicates a cast going up the class tree (to an ancestor) and "downcast" indicates a cast going down the class tree (to a descendent); see e.g. this wiki article.