% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/with_nix.R
\name{with_nix}
\alias{with_nix}
\title{Evaluate Function in R or Shell Command via \code{nix-shell} Environment}
\usage{
with_nix(
  expr,
  program = c("R", "shell"),
  project_path = ".",
  message_type = c("simple", "quiet", "verbose")
)
}
\arguments{
\item{expr}{Single R function or call, or character vector of length one with
shell command and possibly options (flags) of the command to be invoked.
For \code{program = R}, you can both use a named or an anonymous function.
The function provided in \code{expr} should not evaluate when you pass arguments,
hence you need to wrap your function call like
\code{function() your_fun(arg_a = "a", arg_b = "b")}, to avoid evaluation and make
sure \code{expr} is a function (see details and examples).}

\item{program}{String stating where to evaluate the expression. Either \code{"R"},
the default, or \code{"shell"}. \code{where = "R"} will evaluate the expression via
\code{RScript} and \code{where = "shell"} will run the system command in \code{nix-shell}.}

\item{project_path}{Path to the folder where the \code{default.nix} file resides.
The default is \code{"."}, which is the working directory in the current R
session. This approach also useful when you have different subfolders
with separate software environments defined in different \code{default.nix} files.}

\item{message_type}{String how detailed output is. Currently, there is
either \code{"simple"} (default), \verb{"quiet} or \code{"verbose"}, which shows the script
that runs via \code{nix-shell}.}
}
\value{
\itemize{
\item if \code{program = "R"}, R object returned by function given in \code{expr}
when evaluated via the R environment in \code{nix-shell} defined by Nix
expression.
\item if \code{program = "shell"}, list with the following elements:
\itemize{
\item \code{status}: exit code
\item \code{stdout}: character vector with standard output
\item \code{stderr}: character vector with standard error
of \code{expr} command sent to a command line interface provided by a Nix package.
}
}
}
\description{
This function needs an installation of Nix. \code{with_nix()} has two effects
to run code in isolated and reproducible environments.
\enumerate{
\item Evaluate a function in R or a shell command via the \code{nix-shell}
environment (Nix expression for custom software libraries; involving pinned
versions of R and R packages via Nixpkgs)
\item If no error, return the result object of \code{expr} in \code{with_nix()} into the
current R session.
}
}
\details{
\code{with_nix()} gives you the power of evaluating a main function \code{expr}
and its function call stack that are defined in the current R session
in an encapsulated nix-R session defined by Nix expression (\code{default.nix}),
which is located in at a distinct project path (\code{project_path}).

\code{with_nix()} is very convenient because it gives direct code feedback in
read-eval-print-loop style, which gives a direct interface to the very
reproducible infrastructure-as-code approach offered by Nix and Nixpkgs. You
don't need extra efforts such as setting up DevOps tooling like Docker and
domain specific tools like \{renv\} to control complex software environments
in R and any other language. It is for example useful for the following
purposes.
\enumerate{
\item test compatibility of custom R code and software/package dependencies in
development and production environments
\item directly stream outputs (returned objects), messages and errors from any
command line tool offered in Nixpkgs into an R session.
\item Test if evolving R packages change their behavior for given unchanged
R code, and whether they give identical results or not.
}

\code{with_nix()} can evaluate both R code from a nix-R session within
another nix-R session, and also from a host R session (i.e., on macOS or
Linux) within a specific nix-R session. This feature is useful for testing
the reproducibility and compatibility of given code across different software
environments. If testing of different sets of environments is necessary, you
can easily do so by providing Nix expressions in custom \code{.nix} or
\code{default.nix} files in different subfolders of the project.

\code{rix_init()} is run automatically to generate a custom \code{.Rprofile}
file for the subshell in \code{project_dir}. The defaults in that file ensure
that only R packages from the Nix store, that are defined in the subshell
\code{.nix} file are loaded and system's libraries are excluded.

To do its job, \code{with_nix()} heavily relies on patterns that manipulate
language expressions (aka computing on the language) offered in base R as
well as the \{codetools\} package by Luke Tierney.

Some of the key steps that are done behind the scene:
\enumerate{
\item recursively find, classify, and export global objects (globals) in the
call stack of \code{expr} as well as propagate R package environments found.
\item Serialize (save to disk) and deserialize (read from disk) dependent
data structures as \code{.Rds} with necessary function arguments provided,
any relevant globals in the call stack, packages, and \code{expr} outputs
returned in a temporary directory.
\item Use pure \code{nix-shell} environments to execute a R code script
reconstructed catching expressions with quoting; it is launched by commands
like this via \code{{sys}} by Jeroen Ooms:
\verb{nix-shell --pure --run "Rscript --vanilla"}.
}
}
\examples{
\dontrun{
# create an isolated, runtime-pure R setup via Nix
project_path <- "./sub_shell"
rix_init(
  project_path = project_path,
  rprofile_action = "create_missing"
)
# generate nix environment in `default.nix`
rix(
  r_ver = "4.2.0",
  project_path = project_path
)
# evaluate function in Nix-R environment via `nix-shell` and `Rscript`,
# stream messages, and bring output back to current R session
out <- with_nix(
  expr = function(mtcars) nrow(mtcars),
  program = "R", project_path = project_path,
  message_type = "simple"
)

# There no limit in the complexity of function call stacks that `with_nix()`
# can possibly handle; however, `expr` should not evaluate and
# needs to be a function for `program = "R"`. If you want to pass the
# a function with arguments, you can do like this
get_sample <- function(seed, n) {
  set.seed(seed)
  out <- sample(seq(1, 10), n)
  return(out)
}

out <- with_nix(
  expr = function() get_sample(seed = 1234, n = 5),
  program = "R",
  project_path = ".",
  message_type = "simple"
)

## You can also attach packages with `library()` calls in the current R
## session, which will be exported to the nix-R session.
## Other option: running system commands through `nix-shell` environment.
}
}
\seealso{
Other Nix execution:
\code{\link[=nix_build]{nix_build()}}
}
\concept{Nix execution}
