Search code examples
roopr6

Update a dependent field in R6 object when a parent field is updated


I'm new to R6 and object oriented programming, so i'm not sure the right way to even talk about dependencies between fields inside an object.

My objects have fields that are dependent on other fields inside the object. I would like those dependent fields to automatically update when one of the inputs is updated.

I have figured out a manual way of doing this, but thought that there may be a better way. I played around with active fields but i could not get them to work.

This example should make it clear. I have an object quad that takes width and height and calculates area. I would like area to be automatically updated when width or height are updated.

This seems to be one of the things that active fields are intended to achieve, but i couldn't make them work.

For the purpose of exposition i hacked to my goal by including a re-calculation line for self$area in the set method for each field.

How is this supposed to be done?

library(R6)
quad <- R6Class("quad", public =
                list(width = NULL,
                     height = NULL,
                     area = NULL,
                     initialize = function(width, height) {
                         self$width <- width
                         self$height <- height
                         self$area = self$width * self$height
                         self$greet()
                     },
                     set_width = function(W) {
                         self$width <- W
                         self$area = self$width * self$height #hack
                     }, 
                     set_height = function(H) {
                         self$height <- H
                         self$area = self$width * self$height #hack
                     }, 
                     greet = function() {
                         cat(paste0("your quad has area: ", self$area, ".\n"))
                     })
                )
#
> quad1 <- quad$new(5, 5)
your quad has area: 25.

> quad1$set_height(10)
> quad1$area
[1] 50    

Solution

  • An active binding is essentially a function that is invoked without needing to use (), so it looks like a regular field.

    In the example below, area is an active binding and is computed each time you access it.

    library(R6)
    Quad <- R6Class(
      "Quad",
      public = list(
        initialize = function(width, height) {
          self$width <- width
          self$height <- height
        },
        width = NULL,
        height = NULL
      ),
      active = list(
        area = function() {
          self$width * self$height
        }
      )
    )
    
    
    q <- Quad$new(8, 3)
    q$area
    #> [1] 24
    
    q$height <- 5
    q$area
    #> [1] 40