Reputation: 271
I don't understand the point of .First function in R. My reason is any code in .Rprofile will be sourced and executed when R starts up anyway.
this
.First<-function(){
library('devtools')
}
and this
library('devtools')
in .Rprofile have exactly the same effect.
However, here is an example that shows .First can make a difference:
example 1, you can see X11.options()$type correctly becomes Xlib as set in .Rprofile
>> cat .Rprofile
.First <- function() {
library(devtools)
}
setHook(
packageEvent("grDevices", "onLoad"),
function(...) grDevices::X11.options(type="Xlib")
)
>> Rscript -e 'X11.options()$type'
[1] "Xlib"
example 2, you can see X11.options()$type is still cairo, the setHook in .Rprofile didn't take effect
>> cat .Rprofile
library(devtools)
setHook(
packageEvent("grDevices", "onLoad"),
function(...) grDevices::X11.options(type="Xlib")
)
>> Rscript -e 'X11.options()$type'
[1] "cairo"
Thanks!
Upvotes: 8
Views: 3278
Reputation: 1
R sources Rprofile.site
then either project-level or user-level .Rprofile
and then evaluates .First
(Rstudio Support).
.First
is useful to include in Rprofile.site
if you want to trigger an action (site-wide) that is conditional on user or project .Rprofile
.
Example: renv
is activated by project-level .Rprofile
.
This code will print a message indicating the location of the renv
cache or will alert the user that renv
hasn't been activated.
if (interactive()) {
.First <- function() {
if ("RENV_PROJECT" %in% names(Sys.getenv())) {
cat("\nLinked to renv cache:\n")
cat("-", renv::paths$cache(), "\n\n")
} else {
cat("\n*** Warning: you are not in an active renv project! ***\n")
cat("- Some functions may be unavailable.\n\n")
}
}
}
Upvotes: 0
Reputation: 57
One benefit of putting startup code in .First() instead of .RProfile is that you can use local variables which won't stay in your Global environment after .First() completes.
For example, my .First() displays the list of .R files on the project directory in as many columns as will fit:
localFiles <- list.files(pattern = "\\.R$", ignore.case = TRUE)
maxChars <- max(nchar(localFiles))
numCols <- as.integer((options("width")$width-2) / (1 + maxChars)) # how many columns will fit?
fmt <- sprint(" %%-%d", maxChars) # left justified in each column
for (nn in localFiles) {
if ((match(nn, localFiles) %% numCols) == 1) cat(" ") # indent each row
cat(sprint(fmt, nn))
if ((match(nn, localFiles) %% numCols) == 0) cat("\n") # end of row
}
if (length(localFiles) %% numCols != 0) cat("\n") # end last row if not complete
Since this is in .First(), all of the temporary variables get cleaned up when the function returns and the Global environment remains clean.
Upvotes: 2
Reputation: 756
It may be unnecessary but it does provide yet another place to modify the startup. It certainly doesn't hurt having it.
I generally run R in different directories to keep things separated; link to a common .Rprofile; and use .First to tailor the current R run environment to the specific problem I'm working on. If .First action wasn't available I'd have to create one.
Upvotes: 1