rtypeで型安全なR?

とか書いとくと、「型安全」とは何か、みたいなマサカリが飛んできて勉強になりそうなのでこういうタイトルにしています。 型安全って何なのかあまり理解できてないので、コメント欄とかTwitter@yutannihilation)でこっそり教えていただけると助かります。

さて、本題です。Rで関数の引数に型を指定できるパッケージができたらしいです。

github.com

といっても、Experimentってばっちり書いてあるので、そのうち名前も使い方も変わる気がします。使い方を覚えるのは尚早な雰囲気ですが、とりあえず触ってみたところをメモ。

インストール

devtools::install_github("romainfrancois/rtype")

使い方

rtypeでは、function()の代わりにfunction_()を使います。

READMEに載ってる例はこんな感じ。

library(rtype)

f <- function_(integer: n ~ 3L, character: txt ~ "foo", {
    rep(txt, n)
})

nはinterger型でデフォルト値は3Ltxtはcharacter型でデフォルト値は"foo"です。

これを動かしてみます。integer型なので4Lは通りますが4は通りません。

f(4L, "アッハイ")
#> [1] "アッハイ" "アッハイ" "アッハイ" "アッハイ"

f(4, "アッハイ")
#> Error: n is not of type integer

自分で型をつくる

この時実際に呼ばれているのはtype_check()というS3メソッドです。それぞれのクラスに対応する型チェックが行われます。

obj <- ls(envir = asNamespace('rtype'))
grep("^type_check.+", obj, value = TRUE)
#> [1] "type_check"           "type_check.character" "type_check.default"   "type_check.integer"  
#> [5] "type_check.logical"   "type_check.numeric" 

なので、type_check.クラス名()という関数をつくってやれば、それが型のチェックに使われます。例えば、長さ1のintegerのinteger_scalarというクラスがあるとします。そのチェックはこんな感じでできます。

type_check.integer_scalar <- function(value, type, name) {
    if(type != "integer_scalar" ||
       is.na(as.integer(value)) ||
       length(value) != 1)
        stop(sprintf("%s is not a valid integer_scalar.", call. = FALSE))
}

f <- function_(integer_scalar:n, {cat(n)})

a <- 1
class(a) <- "integer_scalar"
f(a)
#> 1

b <- 1:3
class(b) <- "integer_scalar"
f(b)
#> Error in type_check.integer_scalar(n, "integer_scalar", "n") : 
#>   FALSE is not a valid integer_scalar.

S3以外のクラスシステムとの親和性は、...よく分からないので誰か書いてください!