Binding behavior changes depending on the invocation (shiny vs. non shiny)

Invocation of the following code has a different binding behavior depending if it's running under shiny or not. I can't justify it in any way.

# app.R
source("b.R")
print("foo after source")
print(foo)
x()
print("setting foo in app.R")
foo <- c(1,2)
x()
# b.R
foo <- c("XXX")

x <- function() {
  print("inside function")
  print(foo)
}

Result if I invoke it with Rscript:

whatever$ Rscript app.R 
[1] "foo after source"
[1] "XXX"
[1] "inside function"
[1] "XXX"
[1] "setting foo in app.R"
[1] "inside function"
[1] 1 2

Result if I invoke it under shiny

> shiny::runApp('whatever')
Loading required package: shiny
[1] "foo after source"
[1] "XXX"
[1] "inside function"
[1] "XXX"
[1] "setting foo in app.R"
[1] "inside function"
[1] "XXX"

It's as if it does early binding for the function closure only when running under shiny.

indeed shiny does have to do all kinds of things with environments to implement the reactivity.

My friendly suggestion would be that it introduces confusion to even attempt to redefine the internals of a function from somewhere else, by reaching into its internals. (i.e. foo). functions receive arguments, so any change to the 'foo' should come from the parameters.

And given that we are entering shiny world, we should consider the pros and cons, of taking some of our functions, and making them reactive()

global variables are not recommended in shinyapps, we have reactiveVal, reactiveValues,eventReactives, reactive, (we also have side effecty observers).

It might help to think of shiny as its own dialect with its own rules.

I hope my comments didn't offend you, it was my attempt to give advice, but I realised I might have been clumsy in how I went about it.

I'm sorry I can't help you. Wish you well with your work.

This topic was automatically closed 54 days after the last reply. New replies are no longer allowed.

This is the textbook approach which holds until you want to coerce a language to have features that other languages have and it doesn't. I know when to break the rules.

if you assume a function to be pure. I am not. I need the function to rely on a global variable because I want the function to have side effects. It's its whole point.

At that point of the code, a shiny app has not even been created. In fact I am not creating one at all.
shiny::runApp is breaking the binding behavior of a function over its closed variables.

Are you aware of shiny's approach to moduralisation, i.e. callModule()
I do appreciate it is frustrating when expectations built up from experience in a language are thwarted by a new package. I don't believe the developers intended headaches, I give them the benefit of the doubt, that the new technology requires its own approach. Reactivity is after all a different paradigm.

I don't care about shiny rules. I am implementing a module loading system, which I have to use in shiny.

I am not offended. Just very disappointed that in R I can't rely on consistent behavior anywhere, despite doing all I can to work around its flawed design.

I am aware of modularization. that's not the kind of modularization I need. I need a system that works like python import.