R: How do I add an extra function to a package? - r

I would like to add an idiosyncratically modified function to package written by someone else, with an R Script, i.e. just for the session, not permanently. The specific example is, let's say, bls_map_county2() added to the blscrapeR package. bls_map_county2 is just a copy of the bls_map_county() function with an added ... argument, for purposes of changing a few of the map drawing parameters. I have not yet inserted the additional parameters. Running the function as-is, I get the error:
Error in BLS_map_county(map_data = df, fill_rate = "unemployed_rate", :
could not find function "geom_map"
I assume this is because my function does not point to the blscrapeR namespace. How do I assign my function to the (installed, loaded) blscrapeR namespace, and is there anything else I need to do to let it access whatever machinery from the package it requires?

When I am hacking on a function in a particular package that in turn calls other functions I often use this form after the definition:
mod_func <- function( args) {body hacked}
environment(mod_func) <- environment(old_func)
But I think the function you might really want is assignInNamespace. These methods will allow the access to non-exported functions in loaded packages. They will not however succeed if the package is not loaded. So you may want to have a stopifnot() check surrounding require(pkgname).

There are two parts to this answer - first a generic answer to your question, and 2nd a specific answer for the particular function that you reference, in which the problem is something slightly different.
1) generic solution to accessing internal functions when you edit a package function
You should already have access to the package namespace, since you loaded it, so it is only the unexported functions that will give you issues.
I usually just prepend the package name with the ::: operator to the non exported functions. I.e., find every instance of a call to some_internal_function(), and replace it with PackageName:::some_internal_function(). If there are several different internal functions called within the function you are editing, you may need to do this a few times for each of the offending function calls.
The help page for ::: does contain these warnings
Beware -- use ':::' at your own risk!
and
It is typically a design mistake to use ::: in your code since the
corresponding object has probably been kept internal for a good
reason. Consider contacting the package maintainer if you feel the
need to access the object for anything but mere inspection.
But for what you are doing, in terms of temporarily hacking another function from the same package for your own use, these warnings should be safe to ignore (at you own risk, of course - as it says in the manual)
2) In the case of blscrapeR ::bls_map_county()
The offending line in this case is
ggplot2::ggplot() + geom_map(...
in which the package writers have specified the ggplot2 namespace for ggplot(), but forgotten to do so for geom_map() which is also part of ggplot2 (and not an internal function in blscrapeR ).
In this case, just load ggplot2, and you should be good to go.
You may also consider contacting the package maintainer to inform them of this error.

Related

What are some use cases for assignInMyNamespace?

I would like to know of some packages that make use of assignInMyNamespace and what it's used for, if it is even advisable to use this function in production code. The help page gives the following information:
assignInMyNamespace is intended to be called from functions within a package, and chooses the namespace as the environment of the function calling it.
However it also gives the following warning about assignInNamespace:
assignInNamespace should not be used in final code, and will in future throw an error if called from a package. Already certain uses are disallowed.
Presumably this is because packages shouldn't ever try to change the namespaces of other packages, which is why the warning doesn't apply to assignInMyNamespace. Is this true?
NB: I am developing a package with an unexported testing function that allows any unexported function in the package to be temporarily replaced with one that saves its inputs and outputs. I am also considering such a technique for switching between memoised / un-memoised versions of functions.
EDIT: In practise, assignInMyNamespace only changes unexported functions - not 'any function in the package' as previously stated. I only realised this recently, and it's actually thrown a spanner in the works with the package I'm developing. Therefore I would also be very interested to know if there is a solution to the problem that works across both exported and unexported functions during package use.

S3 - delayed method registration in R

I have an R-package ('mill') with a 'suggest' ('hlt') where I would like to register a new class for an S3 method in 'hlt'. Because the 'hlt' package is a suggest I followed the advise of Hadley and copied a function in 'mill' that registers my new mill::html.pdf_diff_df() function but not until after loading the 'hlt' package:
https://github.com/r-lib/vctrs/blob/master/R/register-s3.R
The problem I encounter is that when I load 'mill' and call the html() method on the new class ('pdf_diff_df') defined in the 'mill' package I just get the error:
html(df)
Error in html(df) : could not find function "html"
I would, of course, like to give the warning that one should load the 'hlt' package at that point, otherwise the user doesn't understand the problem. Any ideas? The source in 'mill' of this new function is here:
https://github.com/SVA-SE/mill/blob/136f372f88d794bb6149922c24dd9a4f731e4c7e/R/images.R#L195-L206
If I understand the situation correctly, hlt exports html(), but you don't want a fixed dependency on hlt, so you can't import html from hlt.
In that case, simply use a fully qualified call to it, i.e. hlt::html(...).
Since hlt is only in Suggests, this should only be called conditional on requireNamespace('hlt') returning TRUE.
Edited to add:
If you want a user to be able to call html(...), you have to export it from your package. This is hard, because it is exported from the hlt package, and if the two packages export different versions, you'll get warnings about one hiding the other.
As far as I know there isn't a nice solution to this, but really, it's not your problem. If a user hasn't attached hlt, they have no reason to expect html(...) to do anything. If they have, it will just work.
If you really want to help out the user without generating new messages, you could export your own function with a different name, something like
html2 <- function(...) {
if (requireNamespace("hlt"))
hlt::html(...)
else
warning("Suggested package 'hlt' must be installed for this to work.")
}

Self-written R package does not find its own function

I created a package with some functions which are helpful at my company. Recently, I restructered the package such that there are helper functions which need not to be accessible for everyone, but are called internally from other (exported) functions of the package. These helper functions are not exported to the namespace (no #' #export in the respective .R files).
Now, when I call one of the "major" (exported) functions, I get the error message (no real function names):
Error in major_function() : could not find function "helper_function"
Im fairly new in building packages, but from what I understood so far (from https://cran.r-project.org/web/packages/roxygen2/vignettes/namespace.html), it should neither be necessary to export the helper functions, nor to add #' importFrom my_package helper_function to the .R file of the major function.
When I tried this, it actually produced errors when checking the package. I also tried to call the helper functions with my_package:::helper_function, but this lead to the note that it should almost never be necessary to call functions from the same package like this.
Maybe useful information:
The error occurs only when I call a major_function_1 which internally calls major_function_2 which calls a helper_function.
I think there is more to your problem than what you state. As long as all your functions are defined in the same namespace (this also means that all your functions need to live in .R files in the same folder), the calling function should find the helper-functions accordingly.
I suspect you have your helper functions nested in some way, and that is causing the problem.
I recommend to recheck your namespace structure, or post a simplistic outline of your package here.
Another reason that could come to mind, is that you do not export your 'mayor_function2' in your NAMESPACE-file in your package root (maybe you have not recompiled the Roxygen documentation generating this file), and additionally have a local shadow of the the calling function 'mayor_function1'. Try to check this and rerun from a clean compile.

Is it unwise to modify the class of functions in other packages?

There's a bit of a preamble before I get to my question, so hang with me!
For an R package I'm working on I'd like to make it as easy as possible for users to partially apply functions inline. I had the idea of using the [] operators to call my partial application function, which I've name "partialApplication." What I aim to achieve is this:
dnorm[mean = 3](1:10)
# Which would be exactly equivalent to:
dnorm(1:10, mean = 3)
To achieve this I tried defining a new [] method for objects of class function, i.e.
`[.function` <- function(...) partialApplication(...)
However, R gives a warning that the [] method for function objects is "locked." (Is there any way to override this?)
My idea seemed to be thwarted, but I thought of one simple solution: I can invent a new S3 class "partialAppliable" and create a [] method for it, i.e.
`[.partialAppliable` = function(...) partialApplication(...)
Then, I can take any function I want and append 'partialAppliable' to its class, and now my method will work.
class(dnorm) = append(class(dnorm), 'partialAppliable')
dnorm[mean = 3](1:10)
# It works!
Now here's my question/problem: I'd like users to be able to use any function they want, so I thought, what if I loop through all the objects in the active environment (using ls) and append 'partialAppliable' to the class of all functions? For instance:
allobjs = unlist(lapply(search(), ls))
#This lists all objects defined in all active packages
for(i in allobjs) {
if(is.function(get(i))) {
curfunc = get(i)
class(curfunc) = append(class(curfunc), 'partialAppliable')
assign(i, curfunc)
}
}
VoilĂ ! It works. (I know, I should probably assign the modified functions back into their original package environments, but you get the picture).
Now, I'm not a professional programmer, but I've picked up that doing this sort of thing (globally modifying all variables in all packages) is generally considered unwise/risky. However, I can't think of any specific problems that will arise. So here's my question: what problems might arise from doing this? Can anyone think of specific functions/packages that will be broken by doing this?
Thanks!
This is similar to what the Defaults package did. The package is archived because the author decided that modifying other package's code is a "very bad thing". I think most people would agree. Just because you can do it, does not mean it's a good idea.
And, no, you most certainly should not assign the modified functions back to their original package environments. CRAN does not like when packages modify the users search path unnecessarily, so I would be surprised if they allowed a package to modify other package's function arguments.
You could work around that by putting all the modified functions in an environment on the search path. But then you have to ensure that environment is always searched first, which means modifying the search path every time another package is loaded.
Changing arguments for functions in other packages also has the potential to make it very difficult for others to reproduce your results because they must have all your argument settings. Unless you always call functions with all their arguments specified, which defeats the purpose of what you're trying to do.

Building R package: no visible global function definition for 'subject'

I'm building an R package for the first time and am having some trouble. I am doing an R CMD Check and am getting the following error:
get.AlignedPositions: no visible global function definition for 'subject'
I am not sure what is causing this. I don't even have a "subject" variable in my code. The code is rather lengthy so I rather not paste all of it unless someone asks in a comment. Is there something specific I should look for? The only thing I can think of is that I have a line like this:
alignment <-pairwiseAlignment(pattern = canonical.protein, subject=protein.extracted, patternQuality=patternQuality,
subjectQuality=subjectQuality,type = type, substitutionMatrix= substitutionMatrix,
fuzzyMatrix=fuzzyMatrix,gapOpening=gapOpening,gapExtension=gapExtension,
scoreOnly=scoreOnly)
but subject is defined by the pairwiseAlignment function in the Biostrings package. Thank you for your help!
R spotted a function, subject, being used without a function called subject being available. One possible reason for this is explained in this discussion on R-devel. In that case code is used conditionally, e.g. if a certain package is installed we use its functionality. When checking the package on a system which does not have this package installed, we run in to these kinds of warnings. So please check if this might be the case. Alternatively, you might have made a mistake by calling subject while no function existed, e.g. subject was not a function but just an object.

Resources