Attaching a temporary namespace to the search path
Asked Answered
S

1

3

This question is sort of a follow up to this post as I'm still not fully convinced that, with respect to code robustness, it wouldn't be far better to make typing namespace::foo() habit instead of just typing foo() and praying you get the desired result ;-)

Actual question

I'm aware that this goes heavily against "standard R conventions", but let's just say I'm curious ;-) Is it possible to attach a temporary namespace to the search path somehow?


Motivation

At a point where my package mypkg is still in "devel stage" (i.e. not a true R package yet):

  • I'd like to source my functions into an environment mypkg instead of .GlobalEnv
  • then attach mypkg to the search path (as a true namespace if possible)
  • in order to be able to call mypkg::foo()

I'm perfectly aware that calling :: has its downsides (it takes longer than simply typing a function's name and letting R handle the lookup implicitly) and/or might not be considered necessary due to the way a) R scans through the search path and b) packages may import their dependencies (i.e. using "Imports" instead of "Depends", not exporting certain functions etc). But I've seen my code crash at least twice due to the fact that some package has overwritten certain (base) functions, so I went from "blind trust" to "better-to-be-safe-than-sorry" mode ;-)

What I tried

AFAIU, namespaces are in principle nothing more than some special kind of environment

> search()
[1] ".GlobalEnv"        "package:stats"     "package:graphics" 
[4] "package:grDevices" "package:utils"     "package:datasets" 
[7] "package:methods"   "Autoloads"         "package:base"     

> asNamespace("base")
<environment: namespace:base>

And there's the attach() function that attaches objects to the search path. So here's what I thought:

temp.namespace <- new.env(parent=emptyenv())
attach(temp.namespace)
> asNamespace("temp.namespace")
Error in loadNamespace(name) : 
  there is no package called 'temp.namespace'

I guess I somehow have to work with attachNamepace() and figure out what this expects before it is called in in library(). Any ideas?


EDIT

With respect to Hadley's comment: I actually wouldn't care whether the attached environment is a full-grown namespace or just an ordinary environment as long as I could extend :: while keeping the "syntactic sugering" feature (i.e. being able to call pkg::foo() instead of "::"(pkg="pkg", name="foo")()).

This is how function "::" looks like:

> get("::")
function (pkg, name) 
{
    pkg <- as.character(substitute(pkg))
    name <- as.character(substitute(name))
    getExportedValue(pkg, name)
}

This is what it should also be able to do in case R detects that pkg is in fact not a namespace but just some environment attached to the search path:

"::*" <- function (pkg, name) 
{
    pkg <- as.character(substitute(pkg))
    name <- as.character(substitute(name))
    paths <- search()
    if (!pkg %in% paths) stop(paste("Invalid namespace environment:", pkg))
    pos <- which(paths == pkg)
    if (length(pos) > 1) stop(paste("Multiple attached envirs:", pkg))
    get(x=name, pos=pos)
}

It works, but there's no syntactic sugaring:

> "::*"(pkg="tempspace", name="foo")
function(x, y) x + y
> "::*"(pkg="tempspace", name="foo")(x=1, y=2)
[1] 3

How would I be able to call pkg::*foo(x=1, y=2) (disregarding the fact that ::* is a really bad name for a function ;-))?

Serration answered 25/3, 2013 at 16:52 Comment(6)
Note from the help file for asNamespace: Internal namespace support functions. Not intended to be called directly.Salomo
As I understand it, the package devtools does more or less what you describe. You can read the load_ALL() to get some idea how @hadley solved this.Soutane
It's not clear what you're trying to do - I think you probably just want to attach the environment, and not worry about manually creating a namespace by hand (which is v. difficult, but done in devtools albeit in a fragile way)Eton
@hadley: I'd like to source my functions into mynamespace (instead of .GlobalEnv) in order to be able to call mynamespace::foo() instead of foo(). If my code had already turned into a full grown package, there'd be no problem. But at "devel stage" I can't use :: as there is no real namespace yet. Thus the question if I can create a temporary one.Serration
Just use devtools, which does all that for you. If you want to do it yourself, you'll have to read the source code ;)Eton
@hadley: thanks, I'll have a look at devtoolsSerration
B
1

There is something wrong in your motivation: your namespace does NOT have to be attached to the search path in order to use the '::' notation, it is actually the opposite.

The search path allows symbols to be picked by looking at all namespaces attached to the search path.

So, as Hadley told you, you just have to use devtools::load_all(), that's all...

Bullfight answered 3/7, 2013 at 9:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.