Skip to content

Function that works within a package and not when copied in global environment. Why?

5 messages · Marc Girondot, Bert Gunter, William Dunlap

#
Dear experts,

In the package nlWaldTest, there is an hidden function : .smartsub

I can use it, for example:

 > getFromNamespace(".smartsub", ns="nlWaldTest")(pat="x", repl="b" ,
x="essai(b[1], b[2], x[1])")
[1] "essai(b[1], b[2], b[1])"

Now I try to create this function in my global environment:
smartsub <- getFromNamespace(".smartsub", ns="nlWaldTest")

It works also:
 > smartsub(pat="x", repl="b" , x="essai(b[1], b[2], x[1])")
[1] "essai(b[1], b[2], b[1])"

But if I create the function manually:
 > smartsub2 <- function (pat, repl, x)
  {
      args <- lapply(as.list(match.call())[-1L], eval, parent.frame())
      names <- if (is.null(names(args)))
          character(length(args))
      else names(args)
      dovec <- names %in% vectorize.args
      do.call("mapply", c(FUN = FUN, args[dovec], MoreArgs = 
list(args[!dovec]),
                          SIMPLIFY = SIMPLIFY, USE.NAMES = USE.NAMES))
  }
 > smartsub2(pat="x", repl="b" , x="essai(b[1], b[2], x[1])")
Error in names %in% vectorize.args : objet 'vectorize.args' introuvable

It fails because vectorize.args is unknown

Indeed smartsub2 is different from smartsub.
 > identical(smartsub, smartsub2)
[1] FALSE

1/ Why are they different? They are just a copy of each other.

2/ Second question, vectorize.args is indeed not defined before to be 
used in the function. Why no error is produced in original function?

Thanks a lot

Marc
#
1. No they're not. e.g.
[1] FALSE
function ()
 - attr(*, "srcref")=Class 'srcref'  atomic [1:8] 1 6 1 20 6 20 1 1
  .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile'
<environment: 0x1026578d0>
function ()
 - attr(*, "srcref")=Class 'srcref'  atomic [1:8] 1 6 1 19 6 19 1 1
  .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile'
<environment: 0x102613028>
[1] TRUE

identical() is very fussy about whether t=2 objects are the same; they
must be identical in all respects, including attributes, classes, etc.
attached to them. The namespace object has different attributes
attached to it than the one you created.

2. You need to review how namespaces work. From the "Writing R
extensions" manual:

"The namespace controls the search strategy for variables used by
**functions in the package**. If not found locally, R searches the
package namespace first, then the imports, then the base namespace and
then the normal search path."

So if vectorize.args() is among the package functions, it will be
found by package functions but not by those you write unless
specifically qualified by :: or ::: depending on whether it is
exported.

Cheers,
Bert



Bert Gunter

"The trouble with having an open mind is that people keep coming along
and sticking things into it."
-- Opus (aka Berkeley Breathed in his "Bloom County" comic strip )


On Thu, Feb 2, 2017 at 6:30 AM, Marc Girondot via R-help
<r-help at r-project.org> wrote:
#
Thanks Bert for the explanation about identical.

For the vectorize.args, note that vectorize.args is not a function but 
an variable that is unknown in the namespace nlWaldTest.

 > nlWaldTest::vectorize.args
Erreur : 'vectorize.args' n'est pas un objet export? depuis 
'namespace:nlWaldTest'


Furthermore, if the function is created from a copy of the original one:

smartsub <- getFromNamespace(".smartsub", ns="nlWaldTest")

or if it is created manually: by copy-paste of the code:

smartsub2 <- function (pat, repl, x)
  {
      args <- lapply(as.list(match.call())[-1L], eval, parent.frame())
      names <- if (is.null(names(args)))
          character(length(args))
      else names(args)
      dovec <- names %in% vectorize.args
      do.call("mapply", c(FUN = FUN, args[dovec], MoreArgs = 
list(args[!dovec]),
                          SIMPLIFY = SIMPLIFY, USE.NAMES = USE.NAMES))
  }

Both are defined in the global env, but the first one works and not the 
second one.

I am surprised and don't understand how it is possible.

Sincerely

Marc Girondot
#
OK.

Try:  nlWaldTest:::vectorize.args  (3 colons)

Your error message said it was *not* exported, so you need 3 colons
(which, in general, is a bad idea.It's usually not exported for a
reason).

I presume vectorize.args is is in the environment of the function
copied from the name space, because functions are actually closures
that include (pointers to) their environments; whereas it is certainly
not part of the (global) environment, the environment of the manually
created function by copy-paste of the code.

If I am wrong about this, I would appreciate a correction by someone
with better understanding of such subtleties than I.

-- Bert


Bert Gunter

"The trouble with having an open mind is that people keep coming along
and sticking things into it."
-- Opus (aka Berkeley Breathed in his "Bloom County" comic strip )
On Thu, Feb 2, 2017 at 8:34 AM, Marc Girondot <marc_grt at yahoo.fr> wrote:
#
When searching for the object referred to by a name, R looks first in
the current environment, then in the environment's parent environment,
then in that environment's parent environment, etc.  It stops looking
either when the name is found or when it hits .EmptyEnv, the ultimate
ancestor of all environments.

When a function,f,  is being evaluated, the current environment is a
child of environment(f), which is typically the environment in which
the function was created.  When a function is in a package, the
function's environment is generally the package's namespace. When a
function is made on the command line, its namespace is .GlobalEnv.
.GlobalEnv is not a descendant of any package namespace so when the
function is evaluated it will not look in any package namespace when
looking up names.

You can change the environment of a function with
   environment(f) <- someEnvironment
as in
    environment(revisedLmFunc) <- environment(lm)
or
    environment(revisedLmFunc) <- getNamespace(lm)
I do this when debugging functions in environments, but I don't
recommend it for general use (e.g., for code other people will be
using).

Here is some code that displays the ancestral environments of an
environment to help you look at various situations.

myFormatEnvironment <- function(envir) {
    if (is.null(n <- attr(envir, "name"))) {
        n <- format(envir)
    }
    n
}
ancestralEnvironments <- function (envir) {
    if (identical(envir, emptyenv())) {
        list(envir)
    }
    else {
        c(list(envir), ancestralEnvironments(parent.env(envir)))
    }
}

Here are some examples showing the ancestral environments of a (a)
function in a pacakge, (b) a function in .GlobalEnv, and (c) a
function created by a function in a package.
[1] "<environment: namespace:stats>" "imports:stats"
 [3] "<environment: namespace:base>"  "<environment: R_GlobalEnv>"
 [5] "package:stats"                  "package:graphics"
 [7] "package:grDevices"              "package:utils"
 [9] "package:datasets"               "package:methods"
[11] "Autoloads"                      "<environment: base>"
[13] "<environment: R_EmptyEnv>"
[1] "<environment: R_GlobalEnv>" "package:stats"
 [3] "package:graphics"           "package:grDevices"
 [5] "package:utils"              "package:datasets"
 [7] "package:methods"            "Autoloads"
 [9] "<environment: base>"        "<environment: R_EmptyEnv>"
[1] "<environment: 0x0000000015d33f28>"
 [2] "<environment: namespace:stats>"
 [3] "imports:stats"
 [4] "<environment: namespace:base>"
 [5] "<environment: R_GlobalEnv>"
 [6] "package:stats"
 [7] "package:graphics"
 [8] "package:grDevices"
 [9] "package:utils"
[10] "package:datasets"
[11] "package:methods"
[12] "Autoloads"
[13] "<environment: base>"
[14] "<environment: R_EmptyEnv>"
Bill Dunlap
TIBCO Software
wdunlap tibco.com


On Thu, Feb 2, 2017 at 8:34 AM, Marc Girondot via R-help
<r-help at r-project.org> wrote: