Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a closure to make an assignment active binding in R6

Tags:

r

r6

The active binding tmp_priv_2 does not work as expected:

active_creator <- function(clmn) {   
  function(value) {
    if (missing(value)) {
      private[[paste0(".", clmn)]]
    } else {
      if (!is.na(private[[paste0(".", clmn)]])) 
        warning(paste0("Overriding existing tmp_replace: ", 
                       private[[paste0(".", clmn)]]))
      private[[paste0(".", clmn)]] <- value                
    }
  }
}

tmpClass <-
  R6Class("tmpClass",
          private = list(.tmp_priv = NA_character_),
          active = list(
            tmp_priv = function(value) {         
              clmn <- "tmp_priv"
              if (missing(value)) {
                private[[paste0(".", clmn)]]
              } else {
                if (!is.na(private[[paste0(".", clmn)]])) 
                  warning(paste0("Overriding existing tmp_replace: ", 
                                 private[[paste0(".", clmn)]]))
                private[[paste0(".", clmn)]] <- value
              }       
            },
            tmp_priv_2 = active_creator("tmp_priv"))
  )

TC <- tmpClass$new()

The following results are expected:

> TC$tmp_priv
[1] NA
> TC$tmp_priv <- "asdf"
> TC$tmp_priv <- "zxcv"
Warning message:
In (function (value)  : Overriding existing tmp_replace: asdf

But the following returns an error instead of behaving like the tmp_priv binding:

> TC$tmp_priv_2 <- "asdf"
Error in (function (value)  : object 'clmn' not found

Yet:

> as.list(environment(tmpClass$active$tmp_priv_2))
$clmn
[1] "tmp_priv"

The idea of using one function to define all the active bindings is related to: Function for standard active binding in R6

EDIT (2025-07-30): After receiving the two very helpful answers, comments, and some debugging with Claude AI, I found this old github issue which discusses in great detail closures and R6 active bindings. In short, it is exactly as user2554330 describes.

like image 842
Alex Avatar asked Aug 31 '25 03:08

Alex


1 Answers

clmn exists in the function's environment, but it's not available when R6 calls the function. That said, the solution is the same as the linked question:

library(R6)

active_creator <- function(clmn) {
  eval(substitute(function(value) {
    if (missing(value)) {
      private[[paste0(".", clmn)]]
    } else {
      if (!is.na(private[[paste0(".", clmn)]])) {
        warning(paste0("Overriding existing tmp_replace: ", 
                       private[[paste0(".", clmn)]]))
      }
      private[[paste0(".", clmn)]] <- value
    }
  }))
}

tmpClass <- R6Class("tmpClass",
                    private = list(.tmp_priv = NA_character_),
                    active = list(
                      tmp_priv = function(value) {
                        clmn <- "tmp_priv"
                        if (missing(value)) {
                          private[[paste0(".", clmn)]]
                        } else {
                          if (!is.na(private[[paste0(".", clmn)]])) 
                            warning(paste0("Overriding existing tmp_replace: ", 
                                           private[[paste0(".", clmn)]]))
                          private[[paste0(".", clmn)]] <- value
                        }       
                      },
                      tmp_priv_2 = active_creator("tmp_priv")
                    )
)
TC <- tmpClass$new()

TC$tmp_priv
#> [1] NA
TC$tmp_priv <- "asdf"
TC$tmp_priv <- "zxcv"
#> Warning in (function (value) : Overriding existing tmp_replace: asdf

TC$tmp_priv_2 <- "qwerty"  
#> Warning in (function (value) : Overriding existing tmp_replace: zxcv

TC <- tmpClass$new()

TC$tmp_priv_2
#> [1] NA
TC$tmp_priv_2 <- "azerty"
TC$tmp_priv_2 <- "qwertz"
#> Warning in (function (value) : Overriding existing tmp_replace: azerty

TC$tmp_priv <- "zxcv"
#> Warning in (function (value) : Overriding existing tmp_replace: qwertz

Created on 2025-07-30 with reprex v2.1.1

like image 116
M-- Avatar answered Sep 02 '25 18:09

M--