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 ;-))?
asNamespace
: Internal namespace support functions. Not intended to be called directly. – Salomodevtools
does more or less what you describe. You can read theload_ALL()
to get some idea how @hadley solved this. – Soutanemynamespace
(instead of.GlobalEnv
) in order to be able to callmynamespace::foo()
instead offoo()
. 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. – Serrationdevtools
– Serration