2

実際の質問

すべてのクラスが ( ではなく) パッケージの名前空間に存在する必要がある場合、継承構造を保持しながら、相互に継承する一連の R6 クラスを S4 クラスに変換するにはどうすればよいGlobalEnvですか?

詳細

R6 クラスが で定義されていて.GlobalEnv( でソーシングする場合source()など)、setOldClass()で呼び出されている場合は、すべて正常に機能しwhere = .GlobalEnvます。

しかし、パッケージの名前空間内で R6 クラスが定義されている場合 (を呼び出す場合devtools::load_all()など) 、動作させることができません。

での R6 クラスの定義.GlobalEnv:

Object <- R6Class("Object", portable = TRUE, public = list(
  foo = function() "foo")
)
Api <- R6Class("Api", inherit = Object, portable = TRUE,
  public = list(bar = function() "bar")
)
Module <- R6Class("Module", inherit = Api, portable = TRUE,
  public = list(fooBar = function() "fooBar")

setOldClass()で呼び出すwhere = .GlobalEnv( のデフォルトwhere):

setOldClass(c("Object", "R6"))
setOldClass(c("Api", "Object"))
setOldClass(c("Module", "Api"))

R6 クラスがパッケージの名前空間内で定義されている場合 (のdevtools::load_all()代わりに「ソース」を使用する場合などsource())、明示的な を提供することでそれを説明する必要があると想定しましたwhere

where <- if ("package:r6.s4" %in% search()) {
  as.environment("package:r6.s4")
} else {
  .GlobalEnv
}
try(setOldClass(c("Object", "R6"), where = where))
try(setOldClass(c("Api", "Object"), where = where))
try(setOldClass(c("Module", "Api"), where = where))

ただし、次のエラーが表示されます。

setOldClass(c("Module", "Api"), where = where) のエラー: 「Module」の古いスタイルのクラス情報に一貫性がありません。クラスは定義されていますが、「Api」を拡張しておらず、データ部分として有効ではありません


再現性の促進

この問題をできるだけ簡単に再現できるように努めたので、私のGitHub リポジトリr6.s4でパッケージを見つけることができます

エラーを再現するには、実行 (または RStudio でdevtools::load_all()ヒット)する必要があることに注意してCRTL + SHFT + Lください。

また、この単体テストは、何が起こっているのかを理解するのに役立つ場合があります。

4

1 に答える 1

1

私はそれを理解したと思います。

学んだ教訓

  1. 失敗の理由setOldClass(c("Module", "Api"))は、パッケージRcppに同じ名前のクラスが定義されているためです。

    require("R6")
    > getClass("Module")
    Class "Module" [package "Rcpp"]
    
    Slots:
    
      Name:       .xData
    Class: environment
    
    Extends: 
      Class ".environment", directly
    Class "environment", by class ".environment", distance 2, with explicit coerce
    Class "refObject", by class ".environment", distance 3, with explicit coerce
    
  2. この段階ではパッケージが完全にロードされており、引数が指すことができる名前空間環境が存在するため、呼び出すのに最適な場所はsetOldClass()内部のようです。.onAttach()where

    .onAttach <- function(libname, pkgname) {
      where <- as.environment("package:r6.s4")
      clss <- list(
        c("Object", "R6"),
        c("Api", "Object"),
        c("Module2", "Api")
      )
      sapply(clss, function(cls) {
        try(setOldClass(cls, where = where))
      })
    }
    
  3. 内部では、以前のパッケージのロードで.onAttach()設定されたクラスを「オーバーロード」しないように注意する必要があります。setOldClass()そういうわけで、これに沿った何かが理にかなっているかもしれません:

     .onAttach <- function(libname, pkgname) {
      where <- as.environment("package:r6.s4")
      clss <- list(
        c("Object", "R6"),
        c("Api", "Object"),
        c("Module2", "Api")
      )
      sapply(clss, function(cls) {
        idx <- sapply(cls, isClass)
        try(sapply(cls[idx], removeClass, where = where))
        try(setOldClass(cls, where = where))
      })      
    }
    
  4. 単なるクラス名ではなく、実際のジェネレーター オブジェクトR6に依存するアプローチが非常に気に入っています。これにより、他のすべてのパッケージ コンポーネントと同じようにクラスを使用して整理しておくことができるからです。しかし残念ながら、そのパラダイムは、S4 の同等物を 経由で登録すると失われるようです。この種のことは、クラスの名前衝突のリスクが増大しているという以前の不満に戻ります- .::setOldClass()*sigh*

私の試行錯誤のプロセスの詳細に興味がある方へ: パッケージを一種の自己参照に変えようとしました。ファイルR/classes.rtests/testthat/test-S4.r、および名前の競合をチェックして処理する方法に関するいくつかのプロトタイプ コードR/name_clashes.rを確認します。

于 2015-03-20T01:32:34.263 に答える