What Are the Double Colons (::) in R

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 + 2
  • file2.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



Leave a reply



Submit