Repository navigation
Should Pipe Operator Work on Set Functions? #6862
Description
Activity
the set* functions do return their inputs, invisibly:
Line 2742 in bdd7e2d
invisible(x) so piping is natural IMO
Reacted by Benjamin Schwendinger and crisijauss91Oh, I didn't know that before. Does the invisible function avoid copying?
That's right,
invisible()doesn't copy its argument.I am still puzzled by this design. Do all R functions always return something?
yes, though sometimes it's NULL
Reacted by Jan Goreckithe set* functions do return their inputs, invisibly:
Most set* functions do (yes I just look through all 17 exported of them and
setalloccoldoes not)library(data.table) funs = ls("package:data.table") setfuns = funs[startsWith(funs, "set")] setNames(lapply(setfuns, \(x) tail(as.character(body(x)), 1)), setfuns) #> $set #> [1] "invisible(x)" #> #> $setalloccol #> [1] "ans" #> #> $setattr #> [1] "invisible(x)" #> #> $setcolorder #> [1] "invisible(x)" #> #> $setDF #> [1] "invisible(x)" #> #> $setdroplevels #> [1] "invisible(x)" #> #> $setDT #> [1] "invisible(x)" #> #> $setDTthreads #> [1] "if (!missing(percent)) {\n if (!missing(threads)) \n stopf(\"Provide either threads= or percent= but not both\")\n if (length(percent) != 1) \n stopf(\"percent= is provided but is length %d\", length(percent))\n percent = as.integer(percent)\n if (is.na(percent) || percent < 2 || percent > 100) \n stopf(\"percent==%d but should be a number between 2 and 100\", percent)\n invisible(.Call(CsetDTthreads, percent, restore_after_fork, TRUE, as.integer(throttle)))\n} else {\n invisible(.Call(CsetDTthreads, as.integer(threads), restore_after_fork, FALSE, as.integer(throttle)))\n}" #> #> $setindex #> [1] "FALSE" #> #> $setindexv #> [1] "if (is.list(cols)) {\n sapply(cols, setkeyv, x = x, verbose = verbose, physical = FALSE)\n return(invisible(x))\n} else {\n setkeyv(x, cols, verbose = verbose, physical = FALSE)\n}" #> #> $setkey #> [1] "setkeyv(x, cols, verbose = verbose, physical = physical)" #> #> $setkeyv #> [1] "invisible(x)" #> #> $setnafill #> [1] "invisible(.Call(CnafillR, x, type, fill, nan_is_na(nan), TRUE, cols))" #> #> $setnames #> [1] "invisible(x)" #> #> $setNumericRounding #> [1] ".Call(CsetNumericRounding, as.integer(x))" #> #> $setorder #> [1] "setorderv(x, cols, order, na.last)" #> #> $setorderv #> [1] "invisible(x)"
When pipe operator is used after setnames() vs. setindex(), one would expect a similar effect, which is not the case because the former returns a data.data and the latter returns a logical value.
I feel like the C++ design of pipe operator is more consistent and sensible.
that code to pull the return value is a bit sloppy, that
FALSEis actually an argument value:Line 11 in bdd7e2d
setindex = function(...) setkey(..., physical=FALSE) setindex()is just a wrapper tosetkey(), in turn tosetkeyv(), which returns its argument (invisibly)So all set* functions return invisible(x)?
Have you read the documentation? Please help to flag any help pages that are lacking in explanation, examples, or clarity.
When pipe operator is used after setnames() vs. setindex(), one would expect a similar effect, which is not the case because the former returns a data.data and the latter returns a logical value.
@waynelapierre Before making that statement, did you try it to make sure it was returning a logical value?
setindex(data.table(a=1), a) |> summary()
In this case, the use of the
|>pipe is moot, the determination of return value can easily be verified withsummary(setindex(data.table(a=1), a))(which is what|>is doing underneath).There is precedence in several other languages for most
set-like functions being chainable like this. (Some actually consider chainableset*()a desirable feature. I count myself in that group :-)Now that you know that almost all
set*()functions invisibly return the data, is there a problem either in the implementation or documented behavior? I think either of those may be more pressing issues to resolve. I'm not discounting that you think it "feels strange", but I don't think that's sufficient atm to making substantive changes.
This code works but feels strange. The reason is that the set functions in data.table do not return anything. In C++, pipe operator only works on functions that return something.
Any clarification would be highly appreciated.