Dear All, I am trying to do the following, and could use some hints. Suppose I have a package called pkgA. pkgA exposes an API that includes setting some options, e.g. pkgA works with color palettes, and the user of the package can define new palettes. pkgA provides an API to manipulate these palettes, including defining them. pkgA is intended to be used in other packages, e.g. in pkgB1 and pkgB2. Now suppose pkgB1 and pkgB2 both set new palettes using pkgA. They might set palettes with the same name, of course, they do not know about each other. My question is, is there a straightforward way to implement pkgA's API, such that pkgB1 and pkgB2 do not interfere? In other words, if pkgB1 and pkgB2 both define a palette 'foo', but they define it differently, each should see her own version of it. I guess this requires that I put something (a function?) in both pkgB1's and pkgB2's package namespace. As I see it, this can only happen when pkgA's API is called from pkgB1 (and pkgB2). So at this time I could just walk up the call tree and put the palette definition in the first environment that is not pkgA's. This looks somewhat messy, and I am probably missing some caveats. Is there a better way? I have a feeling that this is already supported somehow, I just can't find out how. Thanks, Best Regards, Gabor
Options that are local to the package that sets them
9 messages · William Dunlap, Gabor Grothendieck, Martin Morgan +1 more
On Fri, Oct 31, 2014 at 7:34 PM, G?bor Cs?rdi <csardi.gabor at gmail.com> wrote:
Dear All, I am trying to do the following, and could use some hints. Suppose I have a package called pkgA. pkgA exposes an API that includes setting some options, e.g. pkgA works with color palettes, and the user of the package can define new palettes. pkgA provides an API to manipulate these palettes, including defining them. pkgA is intended to be used in other packages, e.g. in pkgB1 and pkgB2. Now suppose pkgB1 and pkgB2 both set new palettes using pkgA. They might set palettes with the same name, of course, they do not know about each other. My question is, is there a straightforward way to implement pkgA's API, such that pkgB1 and pkgB2 do not interfere? In other words, if pkgB1 and pkgB2 both define a palette 'foo', but they define it differently, each should see her own version of it. I guess this requires that I put something (a function?) in both pkgB1's and pkgB2's package namespace. As I see it, this can only happen when pkgA's API is called from pkgB1 (and pkgB2). So at this time I could just walk up the call tree and put the palette definition in the first environment that is not pkgA's. This looks somewhat messy, and I am probably missing some caveats. Is there a better way? I have a feeling that this is already supported somehow, I just can't find out how.
Try the settings package.
You can put the following 3 objects, an environment and 2 functions
that access it, in any package that need some package-specific
storage (say your pkgB1 and pkgB2).
.pkgLocalStorage <- new.env(parent = emptyenv())
assignInPkgLocalStorage <- function(name, object) {
.pkgLocalStorage[[name]] <- object
}
getFromPkgLocalStorage <- function(name, object) {
.pkgLocalStorage[[name]]
}
Leave the environment private and export the functions. Then a user can
use them as
pkgB1::assignInPkgLocalStorage("myPallete", makeAPallete(1,2,3))
pkgB2::assignInPkgLocalStorage("myPallete", makeAPallete(5,6,7))
pkgB1::getFromPkgLocalStorage("myPallete") # get the 1,2,3 pallete
If only one of pkgB1 and pkgB2 is loaded you can leave off the pkgBn::.
A package writer can always leave off the pkgBn:: as well.
Bill Dunlap
TIBCO Software
wdunlap tibco.com
On Fri, Oct 31, 2014 at 4:34 PM, G?bor Cs?rdi <csardi.gabor at gmail.com> wrote:
Dear All, I am trying to do the following, and could use some hints. Suppose I have a package called pkgA. pkgA exposes an API that includes setting some options, e.g. pkgA works with color palettes, and the user of the package can define new palettes. pkgA provides an API to manipulate these palettes, including defining them. pkgA is intended to be used in other packages, e.g. in pkgB1 and pkgB2. Now suppose pkgB1 and pkgB2 both set new palettes using pkgA. They might set palettes with the same name, of course, they do not know about each other. My question is, is there a straightforward way to implement pkgA's API, such that pkgB1 and pkgB2 do not interfere? In other words, if pkgB1 and pkgB2 both define a palette 'foo', but they define it differently, each should see her own version of it. I guess this requires that I put something (a function?) in both pkgB1's and pkgB2's package namespace. As I see it, this can only happen when pkgA's API is called from pkgB1 (and pkgB2). So at this time I could just walk up the call tree and put the palette definition in the first environment that is not pkgA's. This looks somewhat messy, and I am probably missing some caveats. Is there a better way? I have a feeling that this is already supported somehow, I just can't find out how. Thanks, Best Regards, Gabor
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
On Fri, Oct 31, 2014 at 8:10 PM, Gabor Grothendieck
<ggrothendieck at gmail.com> wrote:
[...]
Is there a better way? I have a feeling that this is already supported somehow, I just can't find out how.
Try the settings package.
I could, but I don't see how it would solve my problem. https://github.com/markvanderloo/settings/issues/1 Gabor
On Fri, Oct 31, 2014 at 8:16 PM, William Dunlap <wdunlap at tibco.com> wrote:
You can put the following 3 objects, an environment and 2 functions
that access it, in any package that need some package-specific
storage (say your pkgB1 and pkgB2).
.pkgLocalStorage <- new.env(parent = emptyenv())
assignInPkgLocalStorage <- function(name, object) {
.pkgLocalStorage[[name]] <- object
}
getFromPkgLocalStorage <- function(name, object) {
.pkgLocalStorage[[name]]
}
Leave the environment private and export the functions. Then a user can
use them as
pkgB1::assignInPkgLocalStorage("myPallete", makeAPallete(1,2,3))
pkgB2::assignInPkgLocalStorage("myPallete", makeAPallete(5,6,7))
pkgB1::getFromPkgLocalStorage("myPallete") # get the 1,2,3 pallete
I am trying to avoid requiring pkgBn to do this kind of magic. I just want it to call function(s) from pkgA. But maybe something like this would work. In pkgBn: my_palettes <- pkgA::palette_factory() and my_palettes is a function or an environment that has the API functions to modify my_palettes itself (via closure if it is a function), e.g. my_palettes$add_palette(...) my_palettes$get_palette(...) or if it is a function, then my_palettes(add(...), ...) my_palettes(get(...), ...) etc. This would work, right? I'll try it in a minute. Gabor
If only one of pkgB1 and pkgB2 is loaded you can leave off the pkgBn::. A package writer can always leave off the pkgBn:: as well. Bill Dunlap TIBCO Software wdunlap tibco.com On Fri, Oct 31, 2014 at 4:34 PM, G?bor Cs?rdi <csardi.gabor at gmail.com> wrote:
Dear All, I am trying to do the following, and could use some hints. Suppose I have a package called pkgA. pkgA exposes an API that includes setting some options, e.g. pkgA works with color palettes, and the user of the package can define new palettes. pkgA provides an API to manipulate these palettes, including defining them. pkgA is intended to be used in other packages, e.g. in pkgB1 and pkgB2. Now suppose pkgB1 and pkgB2 both set new palettes using pkgA. They might set palettes with the same name, of course, they do not know about each other. My question is, is there a straightforward way to implement pkgA's API, such that pkgB1 and pkgB2 do not interfere? In other words, if pkgB1 and pkgB2 both define a palette 'foo', but they define it differently, each should see her own version of it. I guess this requires that I put something (a function?) in both pkgB1's and pkgB2's package namespace. As I see it, this can only happen when pkgA's API is called from pkgB1 (and pkgB2). So at this time I could just walk up the call tree and put the palette definition in the first environment that is not pkgA's. This looks somewhat messy, and I am probably missing some caveats. Is there a better way? I have a feeling that this is already supported somehow, I just can't find out how. Thanks, Best Regards, Gabor
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
On Fri, Oct 31, 2014 at 8:43 PM, G?bor Cs?rdi <csardi.gabor at gmail.com> wrote:
On Fri, Oct 31, 2014 at 8:10 PM, Gabor Grothendieck <ggrothendieck at gmail.com> wrote: [...]
Is there a better way? I have a feeling that this is already supported somehow, I just can't find out how.
Try the settings package.
I could, but I don't see how it would solve my problem. https://github.com/markvanderloo/settings/issues/1
Isn't your problem really just that you want multiple sets of settings? That's what settings provides. pkgA would provide a class whose instances are created by the clients. Assuming you wrap this in a function create: inst1 <- create(a = 1, b = 2) where create sets up a settings object and does anything else returning the handle inst1. When you want to do something you would pass the instance to the function or method that actually carries it out. This could be done with any OO system in R without settings but if you are looking for an options type interface which I thought you were then you might be able to leverage that package.
Statistics & Software Consulting GKX Group, GKX Associates Inc. tel: 1-877-GKX-GROUP email: ggrothendieck at gmail.com
On 10/31/2014 05:55 PM, G?bor Cs?rdi wrote:
On Fri, Oct 31, 2014 at 8:16 PM, William Dunlap <wdunlap at tibco.com> wrote:
You can put the following 3 objects, an environment and 2 functions
that access it, in any package that need some package-specific
storage (say your pkgB1 and pkgB2).
.pkgLocalStorage <- new.env(parent = emptyenv())
assignInPkgLocalStorage <- function(name, object) {
.pkgLocalStorage[[name]] <- object
}
getFromPkgLocalStorage <- function(name, object) {
.pkgLocalStorage[[name]]
}
Leave the environment private and export the functions. Then a user can
use them as
pkgB1::assignInPkgLocalStorage("myPallete", makeAPallete(1,2,3))
pkgB2::assignInPkgLocalStorage("myPallete", makeAPallete(5,6,7))
pkgB1::getFromPkgLocalStorage("myPallete") # get the 1,2,3 pallete
I am trying to avoid requiring pkgBn to do this kind of magic. I just want it to call function(s) from pkgA. But maybe something like this would work. In pkgBn: my_palettes <- pkgA::palette_factory() and my_palettes is a function or an environment that has the API functions to modify my_palettes itself (via closure if it is a function), e.g. my_palettes$add_palette(...) my_palettes$get_palette(...) or if it is a function, then my_palettes(add(...), ...) my_palettes(get(...), ...) etc. This would work, right? I'll try it in a minute.
You'll need pkgA to be able to know that pkgB1's invokation is to use pkgB1's
parameters, so coupling state (parameters) with function, i.e., a class with
methods. So a solution is to use an S4 or reference class and generator to
encapsulate state and dispatch to appropriate functions, E.g.,
.Plotter <- setRefClass("Plotter",
fields=list(palette="character"),
methods=list(
update(palette) {
.self$palette <- palette
},
plot=function(...) {
graphics::plot(..., col=.self$palette)
}))
APlotter <- function(palette=c("red", "green", "blue"))
.Plotter(palette=palette)
PkgB1, 2 would then
plt = APlotter()
plt$plot(mpg ~ disp, mtcars)
plt$update(c("blue", "green"))
plt$plot(mpg ~ disp, mtcars)
or
.S4Plotter <- setClass("S4Plotter", representation(palette="character")
S4Plotter <- function(palette=c("red", "blue", "green"))
s4plot <- function(x, ...) graphics::plot(..., col=x at palette))
(make s4plot a generic with method for class S4Plotter to enforce type).
Seems like this interface could be generated automatically in .onLoad() of pkgA,
especially if adopting a naming convention of some sort.
Martin
Gabor
If only one of pkgB1 and pkgB2 is loaded you can leave off the pkgBn::. A package writer can always leave off the pkgBn:: as well. Bill Dunlap TIBCO Software wdunlap tibco.com On Fri, Oct 31, 2014 at 4:34 PM, G?bor Cs?rdi <csardi.gabor at gmail.com> wrote:
Dear All, I am trying to do the following, and could use some hints. Suppose I have a package called pkgA. pkgA exposes an API that includes setting some options, e.g. pkgA works with color palettes, and the user of the package can define new palettes. pkgA provides an API to manipulate these palettes, including defining them. pkgA is intended to be used in other packages, e.g. in pkgB1 and pkgB2. Now suppose pkgB1 and pkgB2 both set new palettes using pkgA. They might set palettes with the same name, of course, they do not know about each other. My question is, is there a straightforward way to implement pkgA's API, such that pkgB1 and pkgB2 do not interfere? In other words, if pkgB1 and pkgB2 both define a palette 'foo', but they define it differently, each should see her own version of it. I guess this requires that I put something (a function?) in both pkgB1's and pkgB2's package namespace. As I see it, this can only happen when pkgA's API is called from pkgB1 (and pkgB2). So at this time I could just walk up the call tree and put the palette definition in the first environment that is not pkgA's. This looks somewhat messy, and I am probably missing some caveats. Is there a better way? I have a feeling that this is already supported somehow, I just can't find out how. Thanks, Best Regards, Gabor
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Computational Biology / Fred Hutchinson Cancer Research Center 1100 Fairview Ave. N. PO Box 19024 Seattle, WA 98109 Location: Arnold Building M1 B861 Phone: (206) 667-2793
On Fri, Oct 31, 2014 at 9:20 PM, Gabor Grothendieck
<ggrothendieck at gmail.com> wrote:
[...]
Isn't your problem really just that you want multiple sets of settings? That's what settings provides.
Almost. Multiple sets of settings, set up by the same package. So essentially I want pkgA to use different settings when called from different packages. E.g. imagine that my package igraph has an option that sets a color plot plotting. Another package, qgraph sets this setting to something, say "red". Now another package xgraph wants to set it to "green". What I want is to use "red" when plotting through qgraph, and to use "green" when plotting from xgraph. igraph provides the API for setting this option, so if I just use 'settings' (or a similar technique) in igraph, then xgraph's setting will overwrite qgraph's setting.
pkgA would provide a class whose instances are created by the clients. Assuming you wrap this in a function create: inst1 <- create(a = 1, b = 2) where create sets up a settings object and does anything else returning the handle inst1. When you want to do something you would pass the instance to the function or method that actually carries it out. This could be done with any OO system in R without settings but if you are looking for an options type interface which I thought you were then you might be able to leverage that package.
You are right, this should work. I would like to avoid passing the settings object to each and every igraph function, though. (Would need to update 500+ functions.) But that does not seem to be possible, unless I start messing with the call stack. G.
On Fri, Oct 31, 2014 at 9:26 PM, Martin Morgan <mtmorgan at fredhutch.org> wrote:
[...]
You'll need pkgA to be able to know that pkgB1's invokation is to use
pkgB1's parameters, so coupling state (parameters) with function, i.e., a
class with methods. So a solution is to use an S4 or reference class and
generator to encapsulate state and dispatch to appropriate functions, E.g.,
.Plotter <- setRefClass("Plotter",
fields=list(palette="character"),
methods=list(
update(palette) {
.self$palette <- palette
},
plot=function(...) {
graphics::plot(..., col=.self$palette)
}))
APlotter <- function(palette=c("red", "green", "blue"))
.Plotter(palette=palette)
PkgB1, 2 would then
plt = APlotter()
plt$plot(mpg ~ disp, mtcars)
plt$update(c("blue", "green"))
plt$plot(mpg ~ disp, mtcars)
or
.S4Plotter <- setClass("S4Plotter", representation(palette="character")
S4Plotter <- function(palette=c("red", "blue", "green"))
s4plot <- function(x, ...) graphics::plot(..., col=x at palette))
(make s4plot a generic with method for class S4Plotter to enforce type).
Seems like this interface could be generated automatically in .onLoad() of
pkgA, especially if adopting a naming convention of some sort.
Yes, I think this works, and all three of us came to essentially the same solution. Unfortunately, this solution also requires putting the whole pkgA API inside such a class, otherwise the pkgA functions will not find the right settings. Thanks again! Gabor [...]