Skip to content

Recommended way to call/import functions from a Suggested package

22 messages · Davor Cubranic, David Winsemius, Mark Leeds +6 more

#
Hi Davor,

To the best of my knowledge, there's only one way to use functions
from a suggested package: with require:

if (require("suggested_package")) {
  function_from_suggested_package()
} else {
  stop("suggested package not installed")
}

Unfortunately I don't think there's any way to use a suggested package
without polluting the search path.

Hadley
On Fri, Feb 22, 2013 at 6:26 PM, Davor Cubranic <cubranic at stat.ubc.ca> wrote:

  
    
#
On Feb 22, 2013, at 6:13 PM, Hadley Wickham wrote:

            
I've always wondered: How does lattice manage to use grid functions without putting them on the search path?
#
On Feb 22, 2013, at 6:39 PM, David Winsemius wrote:

            
Maybe I am using the wrong terminology, so here is the behavior I'm referring to:
snipped version an locale ino

attached base packages:
[1] grDevices datasets  splines   graphics  utils     stats     methods   base     

other attached packages:
[1] rms_3.6-2       Hmisc_3.10-1    survival_2.37-2 sos_1.3-5       brew_1.0-6     
[6] lattice_0.20-10

loaded via a namespace (and not attached):
[1] cluster_1.14.3 grid_2.15.2   

Notice that lattice is loaded from my profile
Loading required package: ggplot2
.... snipped version an locale ino
attached base packages:
[1] grDevices datasets  splines   graphics  utils     stats     methods   base     

other attached packages:
[1] ggplot2_0.9.3   rms_3.6-2       Hmisc_3.10-1    survival_2.37-2 sos_1.3-5      
[6] brew_1.0-6      lattice_0.20-10

loaded via a namespace (and not attached):
 [1] cluster_1.14.3     colorspace_1.2-1   dichromat_2.0-0    digest_0.6.0      
 [5] grid_2.15.2        gtable_0.1.2       labeling_0.1       MASS_7.3-22       
 [9] munsell_0.4        plyr_1.8           proto_0.3-10       RColorBrewer_1.0-5
[13] reshape2_1.2.2     scales_0.2.3       stringr_0.6.2
No documentation for ?grid.text? in specified packages and libraries:
you could try ???grid.text?

So at least the help system cannot find a grid function.
starting httpd help server ... done
Error: object 'grid.text' not found

Neither can the R interpreter find it. But it's clearly available if you ask nicely:
function (label, x = unit(0.5, "npc"), y = unit(0.5, "npc"), 
    just = "centre", hjust = NULL, vjust = NULL, rot = 0, check.overlap = FALSE, 
    default.units = "npc", name = NULL, gp = gpar(), draw = TRUE, 
    vp = NULL) 
{
    tg <- textGrob(label = label, x = x, y = y, just = just, 
        hjust = hjust, vjust = vjust, rot = rot, check.overlap = check.overlap, 
        default.units = default.units, name = name, gp = gp, 
        vp = vp)
    if (draw) 
        grid.draw(tg)
    invisible(tg)
}
<bytecode: 0x11617dd50>
<environment: namespace:grid>
#
On Feb 22, 2013, at 9:13 PM, Hadley Wickham wrote:

            
Why -- wouldn't

if (is.function(try(foo::bar, silent=TRUE))) {
  foo::bar(...)
}

do the job?
#
G'day David,

On Fri, 22 Feb 2013 18:50:07 -0800
David Winsemius <dwinsemius at comcast.net> wrote:

            
[...]
Because lattice imports the grid package and has a NAMESPACE (as have
all packages nowadays):

R> packageDescription("lattice")
Package: lattice
Version: 0.20-10
Date: 2012/08/21
[...]
Suggests: grid, KernSmooth, MASS
Imports: grid, grDevices, graphics, stats, utils, methods
[...]

And the relevant information is not in the "Writing R Extensions"
manual but in section 3.5.4 of the "R Language Definition" manual:

	Packages which have a @emph{namespace} have a different search
	path. When a search for an @R{} object is started from an
	object in such a package, the package itself is searched first,
	then its imports, then the base namespace and finally the
	global environment and the rest of the regular search path.
	The effect is that references to other objects in the same
	package will be resolved to the package, and objects cannot be
	masked by objects of the same name in the global environment or
	in other packages.

Thus, as grid is imported by lattice, it is loaded but not attached
(i.e. does not appear in the search path).  However, function in the
lattice package will find functions in the grid package as the imports
are searched.
This will always work, whether the grid package is loaded/attached or
not:

R> sessionInfo()
R version 2.15.2 (2012-10-26)
Platform: x86_64-unknown-linux-gnu/64 (64-bit)

locale:
[1] C

attached base packages:
[1] stats     graphics  grDevices utils     dataset  methods base     

loaded via a namespace (and not attached):
[1] tools_2.15.2
R> grid::grid.text
function (label, x = unit(0.5, "npc"), y = unit(0.5, "npc"), 
    just = "centre", hjust = NULL, vjust = NULL, rot = 0, check.overlap = FALSE, 
    default.units = "npc", name = NULL, gp = gpar(), draw = TRUE, 
    vp = NULL) 
{
    tg <- textGrob(label = label, x = x, y = y, just = just, 
        hjust = hjust, vjust = vjust, rot = rot, check.overlap = check.overlap, 
        default.units = default.units, name = name, gp = gp, 
        vp = vp)
    if (draw) 
        grid.draw(tg)
    invisible(tg)
}
<bytecode: 0x2507c80>
<environment: namespace:grid>


You specifically asked R to get object grid.text from the grid
package, so R obliges to do so.  For the help system to find the help
pages on an object, the package that contains the help pages has to be
on the search path AFAIK.

Cheers,

	Berwin
2 days later
#
Except the double colon method doesn't work (i.e. does not pass R CMD
check) unless you also import the package, which means it's no longer
just a suggestion - it must always be installed.

A simple test case (attached DESCRIPTION and R/test.r) yields this
warning on R CMD check:

* checking for unstated dependencies in R code ... WARNING
'::' or ':::' import not declared from: 'MASS'
See the information on DESCRIPTION files in the chapter 'Creating R
packages' of the 'Writing R Extensions' manual.

Hadley
#
On 02/25/2013 01:28 PM, Hadley Wickham wrote:
haven't been following fully, but loadNamespace rather than require, with 
Suggests: MASS in DESCRIPTION ?

f = function() {
     ok <- tryCatch({
         loadNamespace("MASS")
         TRUE
     }, error=function(...) FALSE)

     if (ok) {
         MASS::huber(1:10)
         cat("OK\n")
     }
}

loadNamespaces loads but does not attach the package. Suggests: is enough to 
quieten the warning with

~/tmp$ R --version
R Under development (unstable) (2013-02-21 r62017) -- "Unsuffered Consequences"

This is consistent with RShowDoc("R-exts") section 1.1.1

   Namespaces accessed by the ?::? and ?:::? operators must be listed here, or 
in ?Suggests? or ?Enhances? (see below).

Martin

  
    
#
I guess that's changed since I last tried it.  (My reproducible
example forgot to include MASS in the Suggests :/ )

Thanks!

Hadley
#
I don't see any warnings if MASS is listed in Suggests in the DESCRIPTION.

Davor
On 2013-02-25, at 1:28 PM, Hadley Wickham wrote:

            
#
Le lundi 25 f?vrier 2013 ? 12:59 -0800, Davor Cubranic a ?crit :
I second that request. As this thread illustrates, most of us do not
know the best way of using functions from suggested packages.

Regards
#
Hi,

So MASS::huber(1:10) seems to do the job i.e. (1) loads the MASS
package (if it's installed), (2) does not pollute the search path,
(3) no 'R CMD check' warning if MASS is listed in Suggests,
and (4) descent error message if MASS is not installed:

   > MASS::huber(1:10)
   Error in loadNamespace(name) : there is no package called ?MASS?

So is this recommendable or are there good reasons for stating in
the manual that "this approach is usually not recommended"? (In which
case it would be good to know what the recommended way is.)

Thanks,
H.
On 02/25/2013 02:56 PM, Hadley Wickham wrote:

  
    
#
On Feb 26, 2013, at 5:47 PM, Herv? Pag?s wrote:

            
But (4) is a problem - it may not fail without MASS since it is only suggested, that's why you need the try() ... The whole thread (which you omitted) was started because the usual if(require(...)) { .. } has an unwanted side-effect which the requestor did not want so the question was what does the job and that got settled before you joined in ...

Cheers,
S
#
On 02/26/2013 03:12 PM, Simon Urbanek wrote:
Not everybody needs the try(). Some functions in my package won't be
able to return anything if the suggested package is not installed, so 
they will have to fail, preferably loudly rather than silently. A pretty 
common situation.

Believe it or not, the discussion was not just about the recommended
way to call functions from the multicore package or another package
providing some kind of support for parallelization, where not having
it installed doesn't diminish the set of functionalities provided by
my package, only makes it slower.
I didn't omit the thread: I actually read every answer including yours.
Are you claiming your solution is the recommended one? Thanks for
clarifying. Also as suggested by some posts in this thread, a
clarification in the official documentation would be nice.

Thanks,
H.

  
    
#
On Feb 26, 2013, at 6:48 PM, Herv? Pag?s wrote:

            
Hmm.. according to the docs that is not quite as intended: a package must work without "suggests" dependencies in the entirety. You can (conditionally) use suggested packages in datasets, examples or vignettes. Obviously, you could be tempted to hide this deficiency (functions failing without suggested packages) by not calling the functions that fail in your examples or tests, but I'd argue that is bypassing the design of "suggests".
I have no assumptions about what the author intended this functionality for, nor what you intend to use it for.
No - but as far as I can see it is in the spirit of the documentation which explicitly mentions if(require(..)) and further down talks about :: and Suggests - so putting 1 + 1 (conditionality and ::) has led me to devise what I suggested.

Cheers,
Simon
#
On 02/26/2013 05:28 PM, Simon Urbanek wrote:
Here is what the "Writing R Extensions" manual says:

   The ?Suggests? field uses the same syntax as ?Depends? and lists
   packages that are not necessarily needed. This includes packages
   used only in examples, tests or vignettes (see Writing package
   vignettes), and *packages loaded in the body of functions*.

Then some examples are provided and they mostly focus on suggested
packages used in examples, tests or vignettes. I couldn't find
anywhere that ``a package must work without "suggests" dependencies
in the entirety''.

AFAIK there are many valid situations where one wants to provide
some additional functionalities in his/her package via suggested
packages. A typical example being a front-end function that supports
different back-ends defined in different packages: the default
back-end will generally be in Depends or Imports, the alternative
back-ends in Suggests. If the user specifies a back-end (thru
some argument passed to the front-end) that is not installed,
then s/he'll get an error.

I don't need to hide this deficiency by not calling the functions
that fail in my examples (or tests). I'll just call my front-end
without specifying an alternative back-end. Not a big deal if my
function generates a plot and if using the alternative back-end
would basically produce the same plot with only some small cosmetic
differences.

Cheers,
H.

  
    
#
On Feb 27, 2013, at 12:54 AM, Herv? Pag?s wrote:

            
*alternative*, yes.

Cheers,
Simon