Skip to content

convert() automatically attempts incorrect sibling casts #509

@mjskay

Description

@mjskay

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:

S7/R/convert.R

Line 106 in db59642

} 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugan unexpected problem or unintended behaviorcoercion 📢

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions