Skip to contents

Several ggdist functions support automatic partial application: when called, if all of their required arguments have not been provided, the function returns a modified version of itself that uses the arguments passed to it so far as defaults. Technically speaking, these functions are essentially "Curried" with respect to their required arguments, but I think "automatic partial application" gets the idea across more clearly.

Functions supporting automatic partial application include:

Partial application makes it easier to supply custom parameters to these functions when using them inside other functions, such as geoms and stats. For example, smoothers for geom_dots() can be supplied in one of three ways:

  • as a suffix: geom_dots(smooth = "bounded")

  • as a function: geom_dots(smooth = smooth_bounded)

  • as a partially-applied function with options: geom_dots(smooth = smooth_bounded(kernel = "cosine"))

Many other common arguments for ggdist functions work similarly; e.g. density, align, breaks, bandwidth, and point_interval arguments.

These function families (except point_interval()) also support passing waivers to their optional arguments: if waiver() is passed to any of these arguments, their default value (or the most recently-partially-applied non-waiver value) is used instead.

Use the auto_partial() function to create new functions that support automatic partial application.

Usage

auto_partial(f, name = NULL, waivable = TRUE)

Arguments

f

A function

name

A character string giving the name of the function, to be used when printing.

waivable

logical: if TRUE, optional arguments that get passed a waiver() will keep their default value (or whatever non-waiver value has been most recently partially applied for that argument).

Value

A modified version of f that will automatically be partially applied if all of its required arguments are not given.

Examples

set.seed(1234)
x = rnorm(100)

# the first required argument, `x`, of the density_ family is the vector
# to calculate a kernel density estimate from. If it is not provided, the
# function is partially applied and returned as-is
density_unbounded()
#> <partial_function>: 
#>   density_unbounded()

# we could create a new function that uses half the default bandwidth
density_half_bw = density_unbounded(adjust = 0.5)
density_half_bw
#> <partial_function>: 
#>   density_unbounded(adjust = 0.5)

# we can overwrite partially-applied arguments
density_quarter_bw_trimmed = density_half_bw(adjust = 0.25, trim = TRUE)
density_quarter_bw_trimmed
#> <partial_function>: 
#>   density_unbounded(adjust = 0.25, trim = TRUE)

# when we eventually call the function and provide the required argument
# `x`, it is applied using the arguments we have "saved up" so far
density_quarter_bw_trimmed(x)
#> 
#> Call:
#> 	density_unbounded(x = x, adjust = 0.25, trim = TRUE)
#> 
#> Data: x (100 obs.);	Bandwidth 'bw' = 0.08864
#> 
#>        x                 y           
#>  Min.   :-2.3457   Min.   :0.009921  
#>  1st Qu.:-1.1220   1st Qu.:0.062906  
#>  Median : 0.1016   Median :0.149478  
#>  Mean   : 0.1016   Mean   :0.201970  
#>  3rd Qu.: 1.3253   3rd Qu.:0.337508  
#>  Max.   : 2.5490   Max.   :0.673160  

# create a custom automatically partially applied function
f = auto_partial(function(x, y, z = 3) (x + y) * z)
f()
#> <partial_function>: 
#>   f()
f(1)
#> <partial_function>: 
#>   f(x = 1)
g = f(y = 2)(z = 4)
g
#> <partial_function>: 
#>   f(y = 2, z = 4)
g(1)
#> [1] 12

# pass waiver() to optional arguments to use existing values
f(z = waiver())(1, 2)  # uses default z = 3
#> [1] 9
f(z = 4)(z = waiver())(1, 2)  # uses z = 4
#> [1] 12