R Project Template

There are many ways to do the same thing. Both options below should produce identical outputs. For opinions on whether to use flakes or not, see the follow sources:

Without Flakes

{
  # Arguments that can be modified by the caller
  system ? builtins.currentSystem,
  # We specify which version of nixpkgs to use as input. This is a specific commit that we can
  # view on GitHub.
  # The revision specified here should be the same as in the flake.lock file generated by the
  # flake.nix file below for the two shells to be identical.
  nixpkgs ? fetchTarball
     # This is the most recent commit on the 23.11 channel as I write this.
     "https://github.com/NixOS/nixpkgs/archive/057f9aecfb71c4437d2b27d3323df7f93c010b7e.tar.gz"
}:

let
  # This pkgs section could also be put into the inputs above,
  # so that the user would be able to also change pkgs.
  pkgs = import nixpkgs {
    inherit system;

    # Unlike with the flake version below, it is important that
    # that we set both config and overlays to maintain purity.
    # If we don't, they try and load the users configs and overlays.
    config = { };

    overlays = [

      (final: prev: {

        # To change a package version, we add an overlay that
        # overrides the rPackages derivation.
        rPackages = prev.rPackages.override {
          overrides = {

            arrow = prev.rPackages.arrow.override {
              # The new version we wish to use
              version = "14.0.2.1";

              # If the hash is unknown, we can leave it blank, add nix
              # will tell us the hash.
              sha256 = "xamUSSjv60UEnJhNn3vM0wHhCMfAs8nYqXhVg6uNNtA=";

              # In this case the new version required an additional
              # dependency in order to build it.
              depends = prev.rPackages.arrow.propagatedBuildInputs
                ++ [prev.cmake];
            };

          };
        };

        # We override the rWrapper to include the R packages
        # that we wish to use.
        rWrapper = prev.rWrapper.override {
          packages = with final.rPackages; [
            arrow # This is our modified arrow package
            data_table
            ggplot2
          ];
        };
      })

      # We could add additional overlays here...

    ];

  };

in
pkgs.mkShellNoCC {

  buildInputs = with pkgs; [
    # We tell the shell to include our R wrapper that includes
    # the packages we want.
    rWrapper
  ];

}

We can then use this shell as follows

$> nix-shell --pure
$> R

R version 4.3.2 (2023-10-31) -- "Eye Holes"
Copyright (C) 2023 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)

> library(arrow)

Attaching package: 'arrow'

> packageVersion("arrow")
[1] '14.0.2.1'
> library(data.table)
data.table 1.14.8 using 8 threads (see ?getDTthreads).  Latest news: r-datatable.com

With Flakes

{
  description = "A modified R wrapper";

  inputs = {
    # Specify which channel to use
    # The exact commit will then be saved in the flake.lock file.
    nixpkgs.url = "github:NixOS/nixpkgs/23.11";
    utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, ... }@inputs: inputs.utils.lib.eachDefaultSystem (system: let
    pkgs = import nixpkgs {

      inherit system;

      overlays = [

        (final: prev: {

          # To change a package version, we add an overlay that
          # overrides the rPackages derivation.
          rPackages = prev.rPackages.override {
            overrides = {

              arrow = prev.rPackages.arrow.override {
                # The new version we wish to use
                version = "14.0.2.1";

                # If the hash is unknown, we can leave it blank, add nix
                # will tell us the hash.
                sha256 = "xamUSSjv60UEnJhNn3vM0wHhCMfAs8nYqXhVg6uNNtA=";

                # In this case the new version required an additional
                # dependency in order to build it.
                depends = prev.rPackages.arrow.propagatedBuildInputs
                  ++ [prev.cmake];
              };

            };
          };

          # We override the rWrapper to include the R packages
          # that we wish to use.
          rWrapper = prev.rWrapper.override {
            packages = with final.rPackages; [
              arrow # This is our modified arrow package
              data_table
              ggplot2
            ];
          };
        })

        # We could add additional overlays here...

      ];
    };
  in {

    # Finally, we set up our development shell
    devShells.default = pkgs.mkShellNoCC {

      packages = with pkgs; [
        # We tell the shell to include our R wrapper that includes
        # the packages we want.
        rWrapper
      ];

    };

  });
}

We can then use this flake as follows

$> nix develop
$> R

R version 4.3.2 (2023-10-31) -- "Eye Holes"
Copyright (C) 2023 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)

> library(arrow)

Attaching package: 'arrow'

> packageVersion("arrow")
[1] '14.0.2.1'
> library(data.table)
data.table 1.14.8 using 8 threads (see ?getDTthreads).  Latest news: r-datatable.com

Up to date information around overriding and building R packages can be found here. The source for building an R package is available in r-modules/default.nix. The generic buildRPackage function is used to pull packages from different sources including CRAN by passing the source to the mkDerive function in the same file.