Skip to content

Friendlier compose() #366

Description

@egnha

It would be nice to have a "friendlier" version of compose() that:

  • sets the formals to that of the first called function
  • is more consistent with tidyverse semantics (i.e., supports splicing and formula syntax)
  • has a more informative print method
  • works as a drop-in replacement

To satisfy these desiderata (the second of which I presume is planned), I have something like the following in mind, which is a minor tweaking of the current implementation:

library(rlang)

compose <- function(...) {
  fs <- lapply(dots_list(...), as_function)
  n <- length(fs)
  
  last <- as_closure(fs[[n]])
  `__call_last` <- function() {
    call_last <- mut_node_car(sys.call(-1), last)
    eval_bare(call_last, parent.frame(2))
  }
  `__rest` <- rev(fs[-n])
  
  set_attrs(
    `formals<-`(
      value = formals(last),
      function() {
        out <- `__call_last`()
        for (f in `__rest`)
          out <- f(out)
        out
      }
    ),
    class = "composite_function"
  )
}

decompose <- function(x) {
  if (!inherits(x, "composite_function"))
    abort("Not a composite function")
    
  environment(x)$fs
}

print.composite_function <- function(x, ...) {
  cat("<composite_function>\n")
  cat("(Listed in calling order)\n")
  
  for (f in rev(decompose(x))) {
    cat("\n")
    print(f)
  }
  
  invisible(x)
}

(The method for formals setting is the same as in #349.)

Example:

foo <- compose(log, ~abs(.) + 1)

foo
#> <composite_function>
#> (Listed in calling order)
#> 
#> function (..., .x = ..1, .y = ..2, . = ..1) 
#> abs(.) + 1
#> 
#> function (x, base = exp(1))  .Primitive("log")

args(foo)
#> function (..., .x = ..1, .y = ..2, . = ..1) 
#> NULL

decompose(foo)
#> [[1]]
#> function (x, base = exp(1))  .Primitive("log")
#> 
#> [[2]]
#> function (..., .x = ..1, .y = ..2, . = ..1) 
#> abs(.x) + 1

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions