Giter VIP home page Giter VIP logo

boomer's People

Contributors

indrajeetpatil avatar krlmlr avatar moodymudskipper avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

boomer's Issues

Indentation for long pipe chains

library(magrittr)

piped <- function(x) {
  x %>% 
    identity() %>% 
    identity() %>% 
    identity()
}

piped <- boomer::rig(piped)

piped(3)
#> 👇 piped
#> 💣 %>%
#> · 💣 identity
#> · · 💣 identity
#> · · · 💣 identity
#> · · · 💥 identity(.)
#> · · · [1] 3
#> · · · 
#> · · 💥 identity(.)
#> · · [1] 3
#> · · 
#> · 💥 identity(.)
#> · [1] 3
#> · 
#> 💥 x %>%  identity() %>%  identity() %>%  identity()
#> [1] 3
#> 
#> 👆 piped
#> [1] 3

Created on 2021-06-17 by the reprex package (v2.0.0)

Is there a way to flatten these? Maybe after reaching a certain depth we could switch to two-digit numbers (and add an extra level only if another function is called)?

Explore intermediate steps using View or a webpage ?

Using View is a hack but would still be helpful,

We'd call boom(subset(head(mtcars, 1 + 1), qsec > 17), view = TRUE) and have this happen, simulated here by :

boomed <- list("subset(head(mtcars, 1 + 1), qsec > 17)" = list(
  " " = subset(head(mtcars, 1 + 1), qsec > 17),
  "head(mtcars, 1 + 1)" = list(
    " " = head(mtcars, 1 + 1),
    "1 + 1" = 1 + 1
  ),
  "qsec > 17" = c(FALSE, TRUE)
))

nm <- names(boomed)
assign(nm, boomed[[1]])
eval(substitute(View(OBJ), list(OBJ = as.symbol(nm))))

boomer_view

This is a bit akward, but technically we can have a nice webpage or shiny app that would do the same.

Instead (or on top) of printing, we'd store results in a nested list, and offer a nice interface to explore it.

I can imagine something with tabs as in {covr}, so if we rig_in_namespace several functions each call would get a tab, so we'd explore nesting by expanding the tree, and recursive rigged functions by clicking on calls that would open the relevant tab, intermediate results would be easy to print or fetch.

Debug failing pipes

worked for me for a failing tidy evaluation pipe, but not for a failing SE pipe. Should we tryCatch() the evaluation?

library(tidyverse)

1 %>%
  identity() %>%
  I() %>%
  boomer::boom()
#> identity(.)
#> [1] 1
#> I(.)
#> [1] 1
#> 1 %>% identity() %>% I()
#> [1] 1

1 %>%
  identity() %>%
  missing_function() %>%
  I() %>%
  boomer::boom()
#> Error in missing_function(.): could not find function "missing_function"

failing_function <- function(x) {
  stop("oops")
}

1 %>%
  identity() %>%
  failing_function() %>%
  I() %>%
  boomer::boom()
#> Error in (function (x) : oops

Created on 2021-04-29 by the reprex package (v2.0.0)

boom to dedicated rstudio tab?

just like the addin for devtools::test() prints to the "build" tab.

It would make it easier to browse long input and not "pollute" the console.

Not sure if RStudio offers this flexibility, we might request if it doesn't.

coverage

Now that we have successful R CMD CHECK and tests, let's keep it this way and work towards 100% coverage.

boom.safe_print option

related to #37

FALSE by default, set to TRUE to have readable output whether the system is UTF8-enabled or not.

Set to TRUE before tests so snapshot tests can work again.

just a global option, no need to make this an argument.

When rigging a function, offer to wrap its function arguments

Now function arguments will be ignored :

image

I don't think there's a way to deal with this automatically, but if we wrap() FUN then we're good :

image

Maybe we can have an additional argument to provide function args, and also get rid of the obsolete message in that case.

Fewer arguments ?

I feel that having many arguments to boom and rig is distracting, maybe some or most would be better only set through options.

I think clock and print might stay, because we'd use them to have a specific behavior in some circumstances.

ignore, visible_only and print_args are more about general preferences, and defaults like ignore = getOption("boom.ignore") don't look good.

max_indent would go there too.

It'd be clearer to document them separately, and then we can have as many options as we want and keep simple usages and doc of boom/rig friendly.

Rigging more than one function

that call each other.

#11 (comment)

I'd like to propose a wire() function:

wire <- function(..., ignore = , ) {
  names <- c(...)

  # rigs all functions
  # ensures that the function environments are updated with the newly rigged functions
}

And also ignite(), to be called from .onLoad():

ignite <- function() {
  config <- Sys.getenv("R_BOOMER")
  ...
  wire(...)  
}

To be used like this:

.onLoad <- function(...) {
  if (requireNamespace("boomer", quietly = TRUE)) {
    boomer::ignite()
  }
}

Visibility?

Should we use withVisible() and optionally hide invisible output (via an argument to rig())? This solves part of #12.

collapse wrapped calls robustly

from #33

There might be other potential problems that the magrittr exception that we hacked a bit brutally.

We could choose to print the top call, then have a way to check if a wrap()ped function was called during execution, then if it wasn't not print the bottom call, but :

  • the top call couldn't be abbreviated using "..."
  • the top call would have only the open emoji

Maybe there's a way

functions rigged with rig_in_namespace don't trigger `signal_rigged_function_and_args()`

When using rig_in_namespace, each function is first rigged in place, then wrappers of all those are set in each enclosing environments.

In doing so we don't provide a mask where to look for the flags for first call or evaluated args.

As a consequence, in the example below we don't signal that we enter add1_wrapper :

fake_package("fake", list(
  add1 = function(x) {
    x + 1
  },
  add1_wrapper = function(x) {
    add1(x)
  },
  rec_factorial = function(x) {
    if(x == 1) return(1)
    x * rec_factorial(x-1)
  }
))

rig_in_namespace(add1, add1_wrapper, print_args = TRUE)

add1_wrapper(1)

image

Optionally print function arguments

I've hit a limitation of boomer today: in a sequence of functions that forward their arguments, the argument values are very hard to trace.

Motivating example:

c <- function(x) wat
b <- function(x) c(x)
b <- boomer::rig(b)
a <- function(x) b(3)

a(5)
#> 👇 b
#> 💣 c
#> 💥 c(x)
#> Error: simpleError/error/condition
#> Error in (function (x) : object 'wat' not found
#> 👆 b

Created on 2021-06-11 by the reprex package (v2.0.0)

How can we enhance the output so that we learn from it that x is 3 in the c(x) call? Can we enhance rig() in a way that it (optionally) prints its arguments? Maybe we can print only those arguments that were actually forced by the function call, to avoid NSE issues?

Execution time of side effects

Should we print first, then execute?

my_print <- function(x) {
  writeLines(paste0("my: ", x))
  invisible(x)
}

boomer::boom(my_print(1 + 2))
#> · 1 + 2
#> [1] 3
#> my: 3
#> my_print(1 + 2)
#> [1] 3

Created on 2021-06-07 by the reprex package (v0.3.0)

I believe my: 3 should come below the my_print() call, this has confused me a few times in the past.

Don't shim <- and {

to avoid repetition of output.

library(boomer)
library(tidyverse)

test <- boomer::rigger() + function() {
  a <- 1 + 2 * 3
}

test()
#> 2 * 3
#> [1] 6
#> 1 + 2 * 3
#> [1] 7
#> a <- 1 + 2 * 3
#> [1] 7
#> {
#>     a <- 1 + 2 * 3
#> }
#> [1] 7
#> [1] 7

Created on 2021-04-26 by the reprex package (v2.0.0)

crayon artifacts

b <- function(x) x
b <- boomer::rig(b)
a <- function(x) b(3)
a <- boomer::rig(a)

a(5)
#> · 👇 b
#> · 💣 crayon::yellow
#> · 💥 crayon::yellow(strrep("· ", globals$n_indent))
#> · [1] "· "
#> · 
#> · 💣 crayon::yellow
#> · 💥 crayon::yellow("a")
#> · [1] "a"
#> · 
#> · 👇 a
#> · 💣 withr::defer_parent
#> · 💥 withr::defer_parent({
#>         cat(dots, "👆 ", crayon::yellow("a"), "\n", sep = "")
#>         mask$..FIRST_CURLY.. <- TRUE
#>     })
#> · $expr
#> · {
#> ·     cat(dots, "👆 ", crayon::yellow("a"), "\n", sep = "")
#> ·     mask$..FIRST_CURLY.. <- TRUE
#> · }
#> · 
#> · $envir
#> · <environment: 0x5634b3576f28>
#> · attr(,"handlers")
#> · attr(,"handlers")[[1]]
#> · attr(,"handlers")[[1]]$expr
#> · {
#> ·     cat(dots, "👆 ", crayon::yellow("b"), "\n", sep = "")
#> ·     mask$..FIRST_CURLY.. <- TRUE
#> · }
#> · 
#> · attr(,"handlers")[[1]]$envir
#> · <environment: 0x5634b3639fd8>
#> · 
#> · 
#> · 
#> · 
#> · 💣 crayon::cyan
#> · 💥 crayon::cyan(deparse1(sc_bkp[[1]]))
#> · [1] "b"
#> · 
#> · 💣 b
#> · 💣 rlang::eval_bare
#> · · 💣 function (...)  {     globals <- getFromNamespace("globals", "boomer")     globals$n_indent <- globals$n_indent + 1     dots <- crayon::yellow(strrep("· ", globals$n_indent))     on.exit({         globals$n_indent <- globals$n_indent - 1     })     sc <- sys.call()     sc_bkp <- sc     sc[[1]] <- function (x)      x     cat(dots, "💣 ", crayon::cyan(deparse1(sc_bkp[[1]])), "\n", sep = "")     success <- FALSE     error <- tryCatch({         res <- withVisible(rlang::eval_bare(sc, parent.frame()))         success <- TRUE     }, error = identity)     if (success && !res$visible && FALSE)          return(invisible(res$value))     cat(dots, "💥 ", crayon::cyan(deparse1(sc_bkp, collapse = paste0("\n", strrep(" ", globals$n_indent + 3)))), "\n", sep = "")     if (!success) {         writeLines(crayon::magenta("Error:", paste0(class(error), collapse = "/")))         stop(error)     }     res <- res$value     print_fun <- getFromNamespace("fetch_print_fun", "boomer")(function (x, ...)      UseMethod("print"), res)     writeLines(c(paste0(dots, capture.output(print_fun(res))), dots))     res }
#> · · 💥 (function (...) 
#>      {
#>          globals <- getFromNamespace("globals", "boomer")
#>          globals$n_indent <- globals$n_indent + 1
#>          dots <- crayon::yellow(strrep("· ", globals$n_indent))
#>          on.exit({
#>              globals$n_indent <- globals$n_indent - 1
#>          })
#>          sc <- sys.call()
#>          sc_bkp <- sc
#>          sc[[1]] <- function (x) 
#>          x
#>          cat(dots, "💣 ", crayon::cyan(deparse1(sc_bkp[[1]])), "\n", sep = "")
#>          success <- FALSE
#>          error <- tryCatch({
#>              res <- withVisible(rlang::eval_bare(sc, parent.frame()))
#>              success <- TRUE
#>          }, error = identity)
#>          if (success && !res$visible && FALSE) 
#>              return(invisible(res$value))
#>          cat(dots, "💥 ", crayon::cyan(deparse1(sc_bkp, collapse = paste0("\n", strrep(" ", globals$n_indent + 3)))), "\n", sep = "")
#>          if (!success) {
#>              writeLines(crayon::magenta("Error:", paste0(class(error), collapse = "/")))
#>              stop(error)
#>          }
#>          res <- res$value
#>          print_fun <- getFromNamespace("fetch_print_fun", "boomer")(function (x, ...) 
#>          UseMethod("print"), res)
#>          writeLines(c(paste0(dots, capture.output(print_fun(res))), dots))
#>          res
#>      })(3)
#> · · [1] 3
#> · · 
#> · 💥 rlang::eval_bare(sc, parent.frame())
#> · [1] 3
#> · 
#> · 💣 crayon::cyan
#> · 💥 crayon::cyan(deparse1(sc_bkp, collapse = paste0("\n", strrep(" ", globals$n_indent + 3))))
#> · [1] "b(3)"
#> · 
#> · 💥 b(3)
#> · · 💣 print_fun
#> · [1] 3
#> · · 💥 print_fun(res)
#> · · [1] 3
#> · · 
#> · [1] 3
#> · 
#> 💣 crayon::yellow
#> 💥 crayon::yellow("a")
#> [1] "a"
#> 
#> · 👆 a
#> [1] 3

Created on 2021-06-11 by the reprex package (v2.0.0)

Cant install it on windows 10 x64

remotes::install_github("moodymudskipper/boomer") does not work on windows, R 4.0.4.

I get: Error: Failed to install 'unknown package' from GitHub:
cannot open the connection.
Backtrace:

  1. remotes::install_github("moodymudskipper/boomer")
  2. remotes:::install_remotes(...)
  3. base::tryCatch(...)
  4. base:::tryCatchList(expr, classes, parentenv, handlers)
  5. base:::tryCatchOne(expr, names, parentenv, handlers[[1L]])
  6. value[3L]

Roll own all.names() implementation

because base and head really shouldn't be part of this:

all.names(quote(base::head(3)))
#> [1] "::"   "base" "head"

Created on 2021-05-02 by the reprex package (v2.0.0)

Not sure if this counts as documented behavior of all.names() .

I wonder if we should memoise double_colon() and triple_colon(), because wrapping occurs at run time here?

Not working with %>%

Hey @moodymudskipper Thanks for your work. boom doesn't seem to work with %>% for me.

image

library(boomer)
library(magrittr)

mtcars %>%
  head(2) %>%
  subset(qsec > 17) %>%
  boom() 

Session Details:

> sessionInfo()
R version 4.0.0 (2020-04-24)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices
[4] utils     datasets  methods  
[7] base     

other attached packages:
[1] magrittr_1.5     
[2] boomer_0.0.0.9000

loaded via a namespace (and not attached):
 [1] ps_1.3.2         
 [2] digest_0.6.25    
 [3] R6_2.4.1         
 [4] reprex_0.3.0     
 [5] evaluate_0.14    
 [6] rlang_0.4.10     
 [7] rstudioapi_0.11  
 [8] fs_1.4.1         
 [9] callr_3.4.3      
[10] whisker_0.4      
[11] rmarkdown_2.6    
[12] tools_4.0.0      
[13] xfun_0.20        
[14] compiler_4.0.0   
[15] processx_3.4.2   
[16] clipr_0.7.0      
[17] htmltools_0.5.1.1
[18] knitr_1.28````

use {styler}

so things like pipe chains and {{ arg }} are displayed nicely

for loops

boomer::boom(for (i in 1:10) i)
#> Error in exists(fun_chr, pf): invalid first argument

Created on 2021-05-25 by the reprex package (v2.0.0)

rigged functions should have a class

c("boomer_rigged", "function")

So we can recognized them easily.

  • original environment is set as an attribute
  • printing method will show it's rigged, and mention original environment/namespace
  • is_rigged function checks if we inherit from "boomer_rigged"

rig_in_namespace will use it to make sure all function environments are synced, it will also make it easy to unrig, just set back the environment to initial value.

pryr message when displaying an arg value for the first time

from : #40

c <- function(x) {
  if (x > 0) wat
}
b <- function(x) c(x)
b <- boomer::rig(b, print_args = TRUE)
a <- function(x) b(3)

a(5)
#> 👇 b
#> 💣 c
#> Registered S3 method overwritten by 'pryr':
#>   method      from
#>   print.bytes Rcpp
#> x :
#> [1] 3
#> 💥 c(x)
#> Error: simpleError/error/condition
#> Error in (function (x) : object 'wat' not found
#> 👆 b

We might find a way to trigger this at a more appropriate time.

On the other hand, we use only function promise_evaled, which has nothing to do with print.bytes(), is unexported, and has really simple code, so it might be cleaner just to have it in the package.

Its cpp code is :

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]`
bool promise_evaled(Symbol name, Environment env) {  
  SEXP object = Rf_findVar(name, env);  return PRVALUE(object) != R_UnboundValue;
}

The R code is :

promise_evaled <- function(name, env) {
  .Call('pryr_promise_evaled', PACKAGE = 'pryr', name, env)
}

I find in the repo the old C code, so we can avoid a Rcpp dependency :

SEXP promise_evaled(SEXP name, SEXP env) {  
  SEXP object = findVar(name, env);  
  return(ScalarLogical(PRVALUE(object) != R_UnboundValue));
}

and the R code would be

.Call("promise_evaled", name, env)

Rigging functions in a package namespace

must be done in .onLoad() .

Consider a package (such as dm) that imports dplyr::filter() . When using boomer::rigger() to rig a function that uses filter() (such as dm_insert_zoomed_outgoing_fks() in cynkra/dm@ee564d1), that function environment's filter shim points to stats::filter() .

The problem is circumvented by adding the following to .onLoad() :

  dm_insert_zoomed_outgoing_fks <<- boomer::rig(dm_insert_zoomed_outgoing_fks)

I'm sorry, it seems this makes rigger() less useful than anticipated.

cli::symbol$ellipsis gives error

cli::symbol$ellipsis
#> [1] "…"
boomer::boom(cli::symbol$ellipsis)
#> cli::symbol$ellipsis
#> Error: simpleError/error/condition
#> Error in .Primitive("$")(cli::symbol, ellipsis): object of type 'closure' is not subsettable

Created on 2021-06-09 by the reprex package (v2.0.0)

Explicit symbols raise an error

fun <- function(x) {
  xx <- rlang::ensym(x)
  xx
}

fun(xx)
#> xx

boomer::rig(fun)(xx)
#> rlang::ensym(x)
#> xx
#> Error in eval(expr, p): object 'xx' not found

Created on 2021-06-03 by the reprex package (v2.0.0)

Are we unquoting implicitly somewhere? How to work around/fix?

Recursive and nested calls of rigged functions not displayed right

Because of the way we store ..FIRST_CALL.. and ..EVALED_ARGS.. in the mask.

fake_package("fake", list(
  add2 = function(a, b) {
    a + b
  }, 
  add4 = function(a, b, c, d) {
    add2(a, b) + add2(c, d)
  },
  rec_factorial = function(x) {
    if(x == 1) return(1)
    x * rec_factorial(x-1)
  } 
))

rig_in_namespace(add2, add4, rec_factorial, print_args = TRUE)

# works
add4(1, 2, 3, 4)

# rigged functions calling themselves are not opened/closed right
add2(add2(1, 2), add2(3, 4))

# recursive calls of rigged functions are not opened/closed right
rec_factorial(2)

works :

image

doesn't :

image

doesn't either:

image

I believe the mask might contain a variable ..STATE.. that would be a list, the first call of a rigged function would add an item to that list, and set up that it would be peeled off on exit (with withr::defer_parent as done atm). then wrap()ped functions would look at the last item.

Nested calls and recursive rigged function calls, readable output ?

There are a few ways to show that something changed (e.g. when we are exploding the calls of a rigged function called by another rigged function) :

  • indent
  • change colors
  • announce explicitly once
  • remind at every step

Right now I tend to think that nested calls should be indented, and that a prefix "my_rigged_function_name>" before printing any call would make it easy to follow along when using rig_in_namespace on several functions that call each other.

I think announcing explicitly just once "booming my_rigged_function_name" is not possible without altering the function's body, which I feel might come back to bite us in some hard to debug corner cases, and bugs in debugging tools are not good for sanity.

Alternative wrapping strategy

Have you considered explicitly wrapping, instead of shimming functions? Are there downsides? I think this would also solve #10.

boom(1 + 2 * 3) could create boomer:::wrapx(1 + boomer:::wrapx(2 * 3)), with wrapx() along the lines of:

wrapx <- function(code) {
  source <- deparse1(unwrapx(substitute(code))) # or we could use the original srcref?

  out <- force(code)
  # side effects
  out
}

unwrapx <- function(expr) {
  # Walk AST to remove boomer:::wrapx() calls for pretty printing
}

The advantage is that we could also boom all inputs. This isn't too important for constants but perhaps much more for variables:

boomer:::wrapx(boomer:::wrapx(1) + boomer:::wrapx(boomer:::wrapx(2) * boomer:::wrapx(3)))

It might simplify the internal code too.

corner cases with function factories

rigged functions have the same code as the original function, however this is not the case of wrapper functions.

So if wrapper functions are returned, or if the rigged function does anything with their body, the result will be unexpected :

image

image

I'm not sure if it can be improved, it's also hard to document.

If the mask contained active bindings rather than wrapper functions, we might make the active binding check how it's used, and if it's not used as a calling function, return the original function, but I haven't figured out how to make the active binding know how it's used.

https://stackoverflow.com/questions/68090266/how-to-have-an-active-binding-know-if-its-called-as-a-function

Easier rigging

Long form:

fun <- function(x) {
  ...
}

# Repetition
fun <- boom::rig(fun)

Wrapping:

# Closing parenthesis is far away
fun <- boom::rig(function(x) {
  ...
})

Reverse pipe:

# Needs %<% in global namespace
fun <- boom::rig() %<% function(x) {
  ...
}

Maybe:

fun <- function(x) {
  ...
}

# with a suitable implementation
boom::xxx(fun)

Or:

# with a suitable implementation
fun <- boom::rigger + function(x) {
  ...
}

Print file + line information

Looks like we can get the srcref from sys.call() ?

a <- function() {
  # dummy comment
  b() # located on the third line of the source file
}

b <- function() {
  call <- sys.call()
  file <- getSrcFilename(call)
  line <- unclass(getSrcref(call))[[1]]
  paste0(file, ":", line)
}

a()
#> [1] "<text>:3"

Created on 2021-06-14 by the reprex package (v2.0.0)

The correct file name is returned when I run locally from a proper file.

boom recursively to error

We would have a function similar to rig but :

  • it rigs recursively the functions we find in the body
  • it doesn't print the boom output right away but sinks it somewhere
  • this boom output is "forgotten" if the call finishes
  • on error, we fetch back all these sunk outputs of unfinished calls and print them back

This way no need for debugonce hell, moreover the case when the error is in a function that was built at runtime (that we can't debugonce from the outside) is especially painful to debug but with this trick would be much less painful.

Looks not easy but useful.

xml experiment

With a minor tweak implemented in https://github.com/moodymudskipper/boomer/tree/xml-experiment we can alter the output so it can be easily collapsed/expanded in an xml editor :

boom(subset(head(mtcars, 2), qsec > 17))
#> <subset CALL=subset(head(mtcars, 2), qsec GT 17)>
#> <head CALL=head(mtcars, 2)>
#>               mpg cyl disp  hp drat    wt  qsec vs am gear carb
#> Mazda RX4      21   6  160 110  3.9 2.620 16.46  0  1    4    4
#> Mazda RX4 Wag  21   6  160 110  3.9 2.875 17.02  0  1    4    4
#> </head>
#> <GT CALL=qsec GT 17>
#> [1] FALSE  TRUE
#> </GT>
#>               mpg cyl disp  hp drat    wt  qsec vs am gear carb
#> Mazda RX4 Wag  21   6  160 110  3.9 2.875 17.02  0  1    4    4
#> </subset>

my_print <- function(x) {
  writeLines(paste0("my: ", x))
  invisible(x)
}

boom(my_print(1 + 2))
#> <my_print CALL=my_print(1 + 2)>
#> <+ CALL=1 + 2>
#> [1] 3
#> </+>
#> my: 3
#> [1] 3
#> </my_print>

It comes with its problem too (readability, multiline, reserved characters), but the idea might be used for something nicer.

The above looks ok in notepad++

Modularize code

Copy/pastes were fine when code was simpler to avoid adding abstraction layers, but now it's starting to be hard to maintain.

`rig_in_namespace()` to be able to reset, add, remove

from #16 :

Right now we need to restart the session (or devtools::load_all() if only rigging in developped package).

Still not sure what would be the best way, maybe we'd need unrig_in_namespace(), that unrigs eveything if no arg is given (we'd need to keep an index of rigged functions), and have rig_in_namespace() add as it does now, but make sure we cannot rig a function 2 times.

Or we might have rig_in_namespace() work a bit like dplyr::select()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.