R namespace access and match.fun

Tag: r , namespaces , package Author: steve_tian Date: 2012-07-31

I'm working on an R package where one of the functions contains a match.fun call to a function in a package that's imported to the package namespace. But on loading the package, the match.fun call can't find the function name. From Hadley Wickham's description I think I'm doing everything right, but this is clearly not the case.

Example:

# in the package file header, for creation of the NAMESPACE via roxygen2:
##` @import topicmodels

# The function declaration in the package
ModelTopics <- function(doc.term.mat, num.topics, topic.method="LDA"){
  topic.fun <- match.fun(topic.method)
  output <- topic.fun(doc.term.mat, k=num.topics)
  return(output)

}    

And then in R:

> library(mypackage)
> sample.output <- ModelTopics(my.dtm, topic.method="LDA", num.topics=5)
Error in get(as.character(FUN), mode = "function", envir = envir) : 
  object 'LDA' of mode 'function' was not found

From my understanding of namespaces, the match.fun call should have access to the package namespace, which should include the topicmodels functions. But that doesn't appear to be the case here. If I import topicmodels directly to the global namespace for the R session, then this works.

Any help is much appreciated. This is R64 2.14.1 running on OSX.

UPDATE: The package is here

Re the DESCRIPTION file, perhaps that's the problem: roxygen2 doesn't update the DESCRIPTION file with Imports: statements. But none of the other packages are listed there, either; only the match.fun calls appear to be affected.

Re the NAMESPACE extract, here's the import section:

import(catspec)
import(foreach)
import(gdata)
import(Hmisc)
import(igraph)
import(lsa)
import(Matrix)
import(plyr)
import(RecordLinkage)
import(reshape)
import(RWeka)
import(stringr)
import(tm)
import(topicmodels)
+1 Nice question. Is your package on github or somewhere we can see the code?
Can you please also add 1) the extract from the NAMESPACE file that shows that topicmodels gets imported, and 2) the extract from DESCRIPTION that shows Imports: topicmodels?

Best Answer

I believe this an issue of scope. Although you have imported topicmodels and thus LDA, you haven't exported these functions, so they aren't available in the search path.

From ?match.fun:

match.fun is not intended to be used at the top level since it will perform matching in the parent of the caller.

Thus, since you are using ModelTopics in the global environment, and LDA isn't available in the global environment, the match.fun call fails.


It seems to me you have two options:

Option 1: use get

An alternative would be to use get where you can specify the environment. Consider this: try to use match.fun to find print.ggplot in the package ggplot2:

match.fun("print.ggplot")
Error in get(as.character(FUN), mode = "function", envir = envir) : 
  object 'print.ggplot' of mode 'function' was not found

Since print.ggplot isn't exported, match.fun can't find it.

However, get does find it:

get("print.ggplot", envir=environment(ggplot))
function (x, newpage = is.null(vp), vp = NULL, ...) 
{
    set_last_plot(x)
    if (newpage) 
        grid.newpage()
    data <- ggplot_build(x)
    gtable <- ggplot_gtable(data)
    if (is.null(vp)) {
        grid.draw(gtable)
    }
    else {
        if (is.character(vp)) 
            seekViewport(vp)
        else pushViewport(vp)
        grid.draw(gtable)
        upViewport()
    }
    invisible(data)
}
<environment: namespace:ggplot2>

Option 2: Export the functions from topicmodels that you need

If you make the necessary functions from topicmodels available in your NAMESPACE, your code should also work.

Do this by either:

  • Selective exporting LDA and other functions to the namespace using @export
  • Declare a dependency, using Depends: topicmodels - this is the same as calling library(topicmodels) in the global environment. This will work, but is possibly a bit of a brute force approach. It also means that any subsequent library call can mask your LDA function, thus leading to unexpected results.

comments:

Thanks, this is very helpful.

Other Answer1

Answering my own question: the DESCRIPTION file wasn't updating the Imports: and Depends: fields after re-roxygenizing the updated code. Hence the match.fun issues. Out of curiousity, why does this affect match.fun but not a range of other function calls made elsewhere to imported package functions?

comments:

Are you using Imports:topicmodels or Depends:topicmodels?
I'm using Depends:topicmodels