r-wakalangで、
よくわからないんですけど、S3のメソッドディスパッチの対象になるのは、
S3method()
で明示的にメソッドとして登録されたもの- サーチパス上にある
関数名.クラス()
というフォーマットの名前の関数で、前者は上書きできない、あるいは先に登録されたものが優先される、とか?
と書いたものの、ほんとかな?と思って調べたのでメモ。
ちょっと長いけど、このへん。
/* This evaluates promises */ PROTECT(top = topenv(R_NilValue, callrho)); val = findFunInEnvRange(method, callrho, top); if(val != R_UnboundValue) { UNPROTECT(1); return val; }
ここでまず環境中を探してるっぽい雰囲気。ここで見つかればreturn val
している。
グローバル環境に同名の関数があればそっちがメソッドディスパッチされるのはこういう順序だからなのかな?
/* We assume here that no one registered a non-function */ if (!s_S3MethodsTable) s_S3MethodsTable = install(".__S3MethodsTable__."); SEXP table = findVarInFrame3(defrho, s_S3MethodsTable, TRUE); if (TYPEOF(table) == PROMSXP) { PROTECT(table); table = eval(table, R_BaseEnv); UNPROTECT(1); } if (TYPEOF(table) == ENVSXP) { val = findVarInFrame3(table, method, TRUE); if (TYPEOF(val) == PROMSXP) { PROTECT(val); val = eval(val, rho); UNPROTECT(1); } if(val != R_UnboundValue) { UNPROTECT(1); return val; } }
次に、なんかよくわからないけど.__S3MethodsTable__.
というのがあって、そこを先に探すらしい。そこで見つかればreturn val
している。
if(lookup_baseenv_after_globalenv) val = findFunWithBaseEnvAfterGlobalEnv(method, ENCLOS(top)); else val = findFunInEnvRange(method, ENCLOS(top), R_EmptyEnv); UNPROTECT(1); return val;
最後に、lookup_baseenv_after_globalenv
というグローバル環境と基本環境どっちを先に探すかというオプション?によって違うけどなにやら環境の中を探して回るらしい。
この.__S3MethodsTable__.
っていうのはまあ内部的に管理されてるS3メソッドのテーブルっぽいからRからはアクセスできないんだろうな...と思ってたら、ふつうにあった。.BaseNamespaceEnv[[".__S3MethodsTable__."]]
でアクセスできるらしい。たとえば、print.Date
はこんな感じ。
get("print.Date", .BaseNamespaceEnv[[".__S3MethodsTable__."]]) #> function (x, max = NULL, ...) #> { #> if (is.null(max)) #> max <- getOption("max.print", 9999L) #> if (max < length(x)) { #> print(format(x[seq_len(max)]), max = max, ...) #> cat(" [ reached getOption(\"max.print\") -- omitted", #> length(x) - max, "entries ]\n") #> } #> else print(if (length(x)) #> format(x) #> else paste(class(x)[1L], "of length 0"), max = max, ...) #> invisible(x) #> } #> <bytecode: 0x000000000ea2fff0> #> <environment: namespace:base>
S3メソッドはここに登録されていくので、exportされてなくてもメソッドディスパッチの対象になる。というからくりらしい。