Returning a list of functions, is this considered an OOP?

Viewed 105

I learned this way from the Johns Hopkins MOOC R Programming a long time ago on Coursera. The idea was to return a list of functions that were defined in a father function's scope. For example:

newString <- function(s) {
    l <- nchar(s)
    return(list(
        get = function() return(s),
        len = function() return(l),
        concate = function(cat) {
            s <<- paste0(s, cat)
            l <<- nchar(s)
        },
        find = function(pattern) return(grepl(pattern, s)),
        substitute = function(pattern, sub) {
            s <<- gsub(pattern, sub, s)
            l <<- nchar(s)
        }
    ))
}

This function returns a list of functions/methods which can manipulate the item "s". I can "new" this "object" calling the father function:

my <- newString("hellow")

And using the "methods" with $ just looks like OOP.

my$get()
# [1] "hellow"
my$len()
# [1] 6
my$substitute("w$", "")
my$get()
# [1] "hello"
my$len()
# [1] 5
my$concate(", world")
my$get()
# [1] "hello, world"
my$find("world$")
# [1] TRUE

To print the "object" directly, we can see it is a list of functions. And all these functions located in the same environment 0x103ca6e08, where the item s was also in.

my
# $get
# function () 
#     return(s)
# <bytecode: 0x1099ac1e0>
#     <environment: 0x103ca6e08>
#     
# $len
# function () 
#     return(l)
# <bytecode: 0x109a58058>
#     <environment: 0x103ca6e08>
#     
# $concate
# function (cat) 
# {
#     s <<- paste0(s, cat)
#     l <<- nchar(s)
# }
# <bytecode: 0x1074fd4e8>
#     <environment: 0x103ca6e08>
#     
# $find
# function (pattern) 
#     return(grepl(pattern, s))
# <bytecode: 0x1076c8470>
#     <environment: 0x103ca6e08>
#     
# $substitute
# function (pattern, sub) 
# {
#     s <<- gsub(pattern, sub, s)
#     l <<- nchar(s)
# }
# <bytecode: 0x1077ad270>
#     <environment: 0x103ca6e08>

So is this style of programming (?) considered OOP or OOP-like? What is the difference of this from S3/S4?


Thanks to @G.Grothendieck, @r2evans and @Jozef. The demo documentation for scoping in R says "functions can encapsulate state information", because of the scoping rules in R. And the RC system "uses environment", so I think what I did was similar to a primitive RC system.


"An object is data with functions. A closure is a function with data." -- John D. Cook

Closures get their name because they enclose the environment of the parent function and can access all its variables.

In http://adv-r.had.co.nz/Functional-programming.html#closures I found the most proper name for this is a closure.

1 Answers

What you are trying to do here reminds me of reference classes or an alternative implementation of the concept by the R6 package - essentially trying to make a OO system similar to other "classic" OOP languages (e.g. Java):

Reference classes:

R6

For example, you could define an R6 class like so:

library(R6)

Person <- R6Class("Person",
  public = list(
    name = NULL,
    hair = NULL,
    initialize = function(name = NA, hair = NA) {
      self$name <- name
      self$hair <- hair
      self$greet()
    },
    set_hair = function(val) {
      self$hair <- val
    },
    greet = function() {
      cat(paste0("Hello, my name is ", self$name, ".\n"))
    }
  )
)

And then one can create an instance from that class:

ann <- Person$new("Ann", "black")

For a quick introduction: https://r6.r-lib.org/articles/Introduction.html

Related