0

In the context of teaching R programming, I am trying to run R scripts completely independently, so that I can compare the objects they have generated.

Currently, I do this with R environments:

student_env <- new.env()
solution_env <- new.env()

eval(parse(text = "x <- 4"), env = student_env)
eval(parse(text = "x <- 5"), env = solution_env)

student_env$x == student_env$y

While this provides some encapsulation, is is by far complete. E.g., if I execute a library() call in the student environment, it is attached to the global R session's search path, making the package available for code running in solution environment as well.

To ensure complete separation, I could fire up subprocesses using the subprocess package:

library(subprocess)
rbin <- file.path(R.home("bin"), "R")
student_handle <- spawn_process(rbin, c('--no-save'))
solution_handle <- spawn_process(rbin, c('--no-save'))

process_write(student_handle, "x <- 4\n")
process_write(solution_handle, "x <- 5\n")

However, I'm not sure how to go about the step of fetching the R objects so I can compare them.

My questions:

  • Is subprocess a good approach?
  • If yes, how can I (efficiently!) grab the R representations of objects from a subprocess so I can compare the objects in the parent process? Python does this through pickling/dilling.
    • I could communicate through .rds files, but this is unnecessary file creation/reading.
    • In R, I came across RProtoBuf, but I'm not sure if it solves my problem.
  • If no, are there other approaches I should consider? I've looked into opencpu, but the concept of firing up a local server and then use R to talk to that server and get representations feels like too complex an approach.

Thanks!

filipsch
  • 195
  • 1
  • 10
  • ... why do you want to do this? – Hong Ooi Apr 27 '18 at 09:24
  • maybe get your student to submit their answers in .Rdata files (created using `save` or `save.image`)? Then you can load their answers into separate env for comparison? – chinsoon12 Apr 27 '18 at 10:17
  • @chinsoon12 I could indeed run each script and save the workspace in an .RData file, after which I import the .RData files into the main R process, but I was wondering if there is a more direct, cleaner way. – filipsch Apr 27 '18 at 10:20

2 Answers2

1

Another possible approach is the callr package, which is popular and developed by a credible source: https://github.com/r-lib/callr#readme.

An example from there:

r(function() var(iris[, 1:4]))

#>              Sepal.Length Sepal.Width Petal.Length Petal.Width
#> Sepal.Length    0.6856935  -0.0424340    1.2743154   0.5162707
#> Sepal.Width    -0.0424340   0.1899794   -0.3296564  -0.1216394
#> Petal.Length    1.2743154  -0.3296564    3.1162779   1.2956094
#> Petal.Width     0.5162707  -0.1216394    1.2956094   0.5810063
  • dash2, Interesting package, thanks for sharing. However, it doesn't allow me to fetch the actual object from the subprocess to use in the parent process. Rather, it prints out the output the call generated in the subprocess. – filipsch Apr 27 '18 at 12:02
  • Er, no? The `r` function "returns the results seamlessly", as the first line of the documentation claims. If it's not working for you, report the bug. –  Apr 27 '18 at 17:12
  • You are right, I am sorry. I'll dig in more to see if there's a way to keep a reference to the child process throughout the execution of a program, so I can fetch objects from it later. Thanks! – filipsch Apr 30 '18 at 11:56
  • 1
    I believe that `callr` runs each command in a separate process and ends it once the command is done. I'd look at the `processx` package, it seems to have a very rich API. It wasn't around at the time when I wrote `subprocess`, but now it seems to have surpassed what my package does. – Lukasz Jul 29 '18 at 08:40
1

I'd use RServe as it lets you run multiple R sessions and control them all from the master R session. You can run commands in those sessions in any given (interwoven) order and access objects stored there in the native format.

subprocess has been created to run and control any arbitrary program via its command line interface, so I have never intended on adding an object-passing mechanism. Although, if I was to access objects from child processes, I'd do it via saveRDS and readRDS.

Lukasz
  • 148
  • 11