R: What do you call the :: and ::: operators and how do they differ?
It turns out there is a unique way to access help info for operators such as these colons: add quotations marks around the operator. [E.g., ?'::'
or help(":::")
].
- Also, instead of quotation marks, back-ticks (i.e, ` ) also work.
Double Colon Operator and Triple Colon Operator
The answer to the question can be found on the help page for "Double Colon and Triple Colon Operators" (see here).
For a package pkg, pkg::name returns the value of the exported variable name in namespace pkg, whereas pkg:::name returns the value of the internal variable name. The package namespace will be loaded if it was not loaded before the call, but the package will not be attached to the search path.
The difference can be seen by examining the code of each:
> `::`
function (pkg, name)
{
pkg <- as.character(substitute(pkg))
name <- as.character(substitute(name))
getExportedValue(pkg, name)
}
<bytecode: 0x00000000136e2ae8>
<environment: namespace:base>
> `:::`
function (pkg, name)
{
pkg <- as.character(substitute(pkg))
name <- as.character(substitute(name))
get(name, envir = asNamespace(pkg), inherits = FALSE)
}
<bytecode: 0x0000000013482f50>
<environment: namespace:base>
:: calls getExportedValue(pkg, name)
, returning the value of the exported variable name
in the package's namespace.
::: calls get(name, envir = asNamespace(pkg), inherits = FALSE)
, searching for the object name
in the Namespace environment of the package, and returning the value of the internal variable name
.
So, what exactly is a namespace?
This site does a good job of explaining the concept of namespaces in R. Importantly:
As the name suggests, namespaces provide “spaces” for “names”. They provide a context for looking up the value of an object associated with a name.
Double colon operator to specify function in do.call
According to the documentation the what
arg of do.call
will take either a function or a non-empty character string naming the function to be called.
If you try implementing the double colon without the quotes it works:
> do.call(base::sum, list(1,2))
[1] 3
So while there is a function named sum
in the package base
, you can't name the function by also specifying the package. Instead, just remove the quotes.
Both require() and double colons in a function
Using require()
and ::
are both ways to access the namespace of a package inside a function. They are not exactly equivalent. They provide different messages.
If you use require()
, you get one set of messages. If the package exists, you get a message that it is attached.
foo <- function(date) {
require(lubridate)
bar <- wday(date)
return(bar)
}
foo(as.Date("2020-1-31"))
Attaching package: ‘lubridate’
[1] 6
If the package does not exist, you get an error.
Error in loadNamespace(name) : there is no package called ‘lubridate’
If you use ::
, you get different behavior. The code is silent if it works.
foo <- function(date) {
bar <- lubridate::wday(date)
return(bar)
}
foo(as.Date("2020-1-31"))
[1] 6
If you do not have the package it generates a warning (not an error).
Warning message:
In library(package, lib.loc = lib.loc, character.only = TRUE, logical.return = TRUE, :
there is no package called ‘lubridate’
Is there an equivalent to using the double colon operator (::) with source() in R?
You can use environments to effect this, using $
in lieu of ::
.
If you have files:
file1.R
func1 <- function(x) x + 1
func2 <- function(y) y + 2file2.R
func3 <- function(x) x + 3
func4 <- function(y) y + 4
then you can create environments for them and load them into there with local=
:
e1 <- new.env()
source("file1.R", local = e1)
e2 <- new.env()
source("file2.R", local = e2)
ls()
# [1] "e1" "e2"
e1$func1(1)
# [1] 2
e1$func2(1)
# [1] 3
e2$func3(1)
# [1] 4
e2$func4(1)
# [1] 5
Note: functions defined in file2.R
will not "see" functions in file1.R
. This has some pros and cons:
Pro: namespace pollution is reduced. If you have constants defined in a file that the functions within it must be able to reference, then this works well. Those constants are in a sense "private" (very loosely speaking) to functions in that same file.
Con: unlike a "package", functions that must see each other must either be defined in the same file or must have another mechanism for determining where to find the other functions.
Why can't I use double colon operator with dplyr when the dataset is in sparklyr?
TL;DR Because your code doesn't use dplyr::if_else
at all.
sparklyr
, when used as in the example, treats Spark as yet another database and issues queries using dbplyr
SQL translation layer.
In this context if_else
is no treated as a function, but an identifier which is converted to SQL primitives:
dbplyr::translate_sql(if_else(PaymentHistory < 0, 0,if_else(PaymentHistory > 1,1, PaymentHistory)))
# <SQL> CASE WHEN ("PaymentHistory" < 0.0) THEN (0.0) WHEN NOT("PaymentHistory" < 0.0) THEN (CASE WHEN ("PaymentHistory" > 1.0) THEN (1.0) WHEN NOT("PaymentHistory" > 1.0) THEN ("PaymentHistory") END) END
However if you pass a fully qualified named, it will circumvent this mechanism, try to evaluate the function, and ultimately fail, because the database columns are not in the scope.
I'm afraid that the name in dplyr will clash with some of the user-defined code.
As you see, there is no need for dplyr to be in scope here at all - functions called in sparklyr
pipelines are either translated to corresponding SQL constructs, or if there is no specific translation rule in place, passed as-is and resolved by Spark SQL engine (this path is used to invoke Spark functions).
Of course this mechanism is not specific to sparklyr
and you're likely to see the same behavior using other table backed by a database:
library(magrittr)
db <- dplyr::src_sqlite(":memory:", TRUE)
dplyr::copy_to(db, mtcars)
db %>% dplyr::tbl("mtcars") %>% dplyr::mutate(dplyr::if_else(mpg < 20, 1, 0))
Error in dplyr::if_else(mpg < 20, 1, 0) : object 'mpg' not found
Using percent operators with double colon in R
Even though quotes work in the double colon case, when referring to an operator like this, you should enclose the operator in single back ticks:
foreach::`%dopar%`
This lets you refer to name anywhere that is not a legal identifier (a legal identifier starts with a letter and is made up of only letters and numbers and underscores).
`%%`(6, 4) # Calling the mod operator in a weird way
`strange %^*&` <- 2 # Defining a weird variable
`strange %^*&` + `strange %^*&` # Using the weird variable
Related Topics
How to Convert Data.Frame Column from Factor to Numeric
Count Number of Rows Matching a Criteria
Assigning Dates to Fiscal Year
How to Force Specific Order of the Variables on the X Axis
Command Lines Error in Rstudio Console
Ggplot2 Heatmaps: Using Different Gradients for Categories
R Strsplit with Multiple Unordered Split Arguments
Wide to Long Multiple Measures Each Time
How to Rotate a Plot in R (Base Graphics)
Printing Newlines with Print() in R
Convert Four Digit Year Values to Class Date
Find Which Interval Row in a Data Frame That Each Element of a Vector Belongs In
Re-Ordering Bars in R's Barplot()
How to Fix the Aspect Ratio in Ggplot