Building a Factory

library(factory)

To build a function factory, begin by writing the function that you want to generalize (like a normal function). Here we’ll generalize a function to add a color scale to a ggplot using a custom palette.

my_scale_color <- function(discrete = TRUE, reverse = FALSE, ...) {
  my_palette <- c(
    "#772277", "#333388", "#1144aa", "#55aa11", 
    "#f40000", "#f47a00", "#ffe314"
  )
  if (reverse) {
    my_palette <- rev(my_palette)
  }
  pal <- colorRampPalette(my_palette, ...)
  
  if (discrete) {
    ggplot2::discrete_scale(
      aesthetics = "colour",
      scale_name = "my_color_scale", 
      palette = pal,
      ...
    )
  } else {
    ggplot2::scale_color_gradientn(colors = pal(256),)
  }
}

ggplot2::ggplot(mtcars) + 
  ggplot2::aes(x = mpg, y = cyl, color = factor(gear)) +
  ggplot2::geom_point() + 
  my_scale_color()
#> Warning: The `scale_name` argument of `discrete_scale()` is deprecated as of ggplot2
#> 3.5.0.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
#> generated.

We could conceivably want to generalize this function to create a similar function, given a palette and (optionally) the name of the scale.

my_scale_color_generic <- function(discrete = TRUE, reverse = FALSE, ...) {
  my_palette <- this_palette
  if (reverse) {
    my_palette <- rev(my_palette)
  }
  pal <- colorRampPalette(my_palette, ...)
  
  if (discrete) {
    ggplot2::discrete_scale(
      aesthetics = "colour",
      scale_name = this_scale_name, 
      palette = pal,
      ...
    )
  } else {
    ggplot2::scale_color_gradientn(colors = pal(256),)
  }
}

We can use factory::build_factory to turn that function into a factory.

my_scale_color_factory <- build_factory(
  fun = my_scale_color_generic,
  this_palette,
  this_scale_name = "my_color_scale"
)

Using our factory with the values we started with should reproduce the original function.

my_scale_color_factory(
  this_palette = c(
    "#772277", "#333388", "#1144aa", "#55aa11", 
    "#f40000", "#f47a00", "#ffe314"
  )
)
#> function (discrete = TRUE, reverse = FALSE, ...) 
#> {
#>     my_palette <- c("#772277", "#333388", "#1144aa", "#55aa11", 
#>     "#f40000", "#f47a00", "#ffe314")
#>     if (reverse) {
#>         my_palette <- rev(my_palette)
#>     }
#>     pal <- colorRampPalette(my_palette, ...)
#>     if (discrete) {
#>         ggplot2::discrete_scale(aesthetics = "colour", scale_name = "my_color_scale", 
#>             palette = pal, ...)
#>     }
#>     else {
#>         ggplot2::scale_color_gradientn(colors = pal(256), )
#>     }
#> }

Note: If you use factory to build a factory in a package, we recommend that you copy/paste the resulting function definition into your package, rather than using the factory::build_factory call directly in your package. This will allow you to better comment your code, and will avoid build errors.