メモ:formulaはそのエンクロージング環境を捕捉している

とかかれているのをふと見かけてなるほどなあと思ったのでメモ。

# formulas automatically capture their enclosing environment
foo <- function(x) {
  y <- 10
  ~ x + y
}

(https://github.com/hadley/rlang/blob/84a63a1a660cebff373b9db02ac37e04c934dbde/R/tidy-eval.R#L30-L34)

※rlangはlazyevalの後継のパッケージ

f <- foo(1)
f
#> ~x + y
#> <environment: 0x000000001b36db90>

と、このオブジェクトを表示しただけではyに値は入っていない。でもyの値はその環境に保存されているので、

lazyeval::f_eval(f)
#> [1] 11

という感じになる。

ちょっと変更して、この環境にアクセスする裏口的な関数increase_y()というのをつくってみる。

bar <- function(x) {
  y <- 10
  increase_y <<- function() {y <<- y+1}
  ~ x + y
}

これを呼び出すとbar()でできたformulaが捕捉している環境のyの値が変わっているのがわかる。

b <- bar(1)
lazyeval::f_eval(b)
#> [1] 11
increase_y()
lazyeval::f_eval(b)
#> [1] 12

考えてみれば当たり前な気もするけど、クロージャよりお手軽に環境を捕捉できるのすごい。便利。

結局いろいろやろうと思うとその環境を操作する手段を用意しないといけないのでそんなことないかも。