[Rd] Conventions: Use of globals and main functions

Martin Maechler m@ech|er @end|ng |rom @t@t@m@th@ethz@ch
Tue Aug 27 11:36:32 CEST 2019


>>>>> Duncan Murdoch 
>>>>>     on Mon, 26 Aug 2019 14:19:36 -0400 writes:

    > On 26/08/2019 1:58 p.m., William Dunlap wrote:
    >> Duncan Murdoch wrote:
    >> > Scripts are for throwaways, not for anything worth keeping.
    >> 
    >> I totally agree and have a tangentially relevant question about the <<- 
    >> operator.  Currently 'name <<- value' means to look up the environment 
    >> stack until you find 'name'  and (a) if you find 'name' in some frame 
    >> bind it to a new value in that frame and (b) if you do not find it make 
    >> a new entry for it in .GlobalEnv.
    >> 
    >> Should R deprecate the second part of that and give an error if 'name' 
    >> is not already present in the environment stack?  This would catch 
    >> misspelling errors in functions that collect results from recursive 
    >> calls.  E.g.,

    > I like that suggestion.  Package tests have been complaining about 
    > packages writing to .GlobalEnv for a while now, so there probably aren't 
    > many instances of b) in CRAN packages; that change might be relatively 
    > painless.

    > Duncan Murdoch

I don't agree currently : AFAICS, there's no other case (in S or) R where an
assignment only works if there's no object with that name.

In addition: If I wanted such a functionality I'd rather have with a
function that has several arguments and this behavior was
switchable via  <argname> = TRUE/FALSE , rather than with
`<<-` which has always exactly 2 arguments.

[This is my personal opinion only; other R Core members may well
 think differently about this]

Martin

    >> collectStrings <- function(list) {
    >>     strings <- character() # to be populated by .collect
    >>     .collect <- function(x) {
    >>         if (is.list(x)) {
    >>             lapply(x, .collect)
    >>         } else if (is.character(x)) {
    >>             strings <<- c(strings, x)
    >>         }
    >>         misspelledStrings <<- c(strings, names(x)) # oops, would like 
    >> to be told about this error
    >>         NULL
    >>     }
    >>     .collect(list)
    >>     strings
    >> }
    >> 
    >> This gives the incorrect:
    >> > collectStrings(list(i="One", ii=list(a=1, b="Two")))
    >> [1] "One" "Two"
    >> > misspelledStrings
    >> [1] "One" "Two" "i"   "ii"
    >> 
    >> instead of what we would get if 'misspelledStrings' were 'strings'.
    >> > collectStrings(list(i="One", ii=list(a=1, b="Two")))
    >> [1] "One" "Two" "a"   "b"   "i"   "ii"
    >> 
    >> If someone really wanted to assign into .GlobalEnv the assign() function 
    >> is available.
    >> 
    >> In S '<<-' only had meaning (b) and R added meaning (a).  Perhaps it is 
    >> time to drop meaning (b).  We could start by triggering a warning about 
    >> it if some environment variable were set, as is being done for 
    >> non-scalar && and ||.
    >> 
    >> Bill Dunlap
    >> TIBCO Software
    >> wdunlap tibco.com <http://tibco.com>
    >> 
    >> 
    >> On Sun, Aug 25, 2019 at 5:09 PM Duncan Murdoch <murdoch.duncan using gmail.com 
    >> <mailto:murdoch.duncan using gmail.com>> wrote:
    >> 
    >> On 25/08/2019 7:09 p.m., Cyclic Group Z_1 wrote:
    >> >
    >> >
    >> > This is a fair point; structuring functions into packages is
    >> probably ultimately the gold standard for code organization in R.
    >> However, lexical scoping in R is really not much different than in
    >> other languages, such as Python, in which use of main functions and
    >> defining other named functions outside of main are encouraged. For
    >> example, in Scheme, from which R derives its scoping rules, the
    >> community generally organizes code with almost exclusively functions
    >> and few non-function global variables at top level. The common use
    >> of globals in R seems to be mostly a consequence of historical
    >> interactive use and, relatedly, an inherited practice from S.
    >> >
    >> > It is true, though, that since anonymous functions (such as in
    >> lapply) play a large part in idiomatic R code, as you put it,
    >> "[l]exical scoping means that all of the problems of global
    >> variables are available to writers who use main()." Nevertheless,
    >> using a main function with other functions defined outside it seems
    >> like a good quick alternative that offers similar advantages to
    >> making a package when functions are tightly coupled to the script
    >> and the project may not be large or generalizable enough to warrant
    >> making a package.
    >> >
    >> 
    >> I think the idea that making a package is too hard is just wrong.
    >> Packages in R have lots of requirements, but nowadays there are tools
    >> that make them easy.  Eleven years ago at UseR in Dortmund I wrote a
    >> package during a 45 minute presentation, and things are much easier now.
    >> 
    >> If you make a complex project without putting most of the code into a
    >> package, you don't have something that you will be able to modify in a
    >> year or two, because you won't have proper documentation.
    >> 
    >> Scripts are for throwaways, not for anything worth keeping.
    >> 
    >> Duncan Murdoch
    >> 
    >> ______________________________________________
    >> R-devel using r-project.org <mailto:R-devel using r-project.org> mailing list
    >> https://stat.ethz.ch/mailman/listinfo/r-devel
    >> 

    > ______________________________________________
    > R-devel using r-project.org mailing list
    > https://stat.ethz.ch/mailman/listinfo/r-devel



More information about the R-devel mailing list