Instantly share code, notes, and snippets.


Cached and Persistent Nix shell with direnv integration

Problem being solved

  • the default direnv Nix integration from projects loads a Nix shell every new terminal or every time shell.nix and/or default.nix changes. On larger projects with a lot of Nix shell dependencies, that can cause the terminal to take more than 6 seconds to load, which significantly degrades the developer experience (DX).
  • when a developer garbage collects in their Nix store, often the Nix shell dependencies are deleted also which causes a slow start the next time the project’s Nix shell is requested, also degrading developer experience (DX).

Solution outline

  • on first Nix shell load, the Nix shell is evaluated fully such that direnv dumps the environment from the spawn Nix shell process

The following shows that the latest nixos-unstable channel has all the transitive dependencies for haskellPackages.lens available for fetching from the binary cache:

$ nix-env -f \
  -iA haskellPackages.lens --dry-run
(dry run; not doing anything)
installing 'lens-4.16.1'
these paths will be fetched (136.28 MiB download, 1899.17 MiB unpacked):

NixOS Installer Ideas


I recently installed NixOS from scratch on a new device and since I haven’t needed to do this in 1.5 years I forgot the sequence needed and made a whole bunch of mistakes.

It would be nice to be able to improve the experience and guide the user. On the other end of the installer UX are assisted installers which often use insecure defaults (e.g. for partitioning, or default passwds, etc).

nixos-help is potentially a valuable tool here but the documentation could use some attention in terms of organization, flow, and ensuring the documentation is updated to the current release of NixOS it is shipped with.

View tlatoolbox.nix
{ stdenv, fetchurl, gtk2, xorg, patchelf, unzip, makeWrapper, jre }:
let version = "1.5.6"; in
stdenv.mkDerivation rec {
inherit version;
name = "TLAToolBox-${version}";
src = fetchurl {
url = "${version}";
sha256 = "1b5sf4d7sv0x1hg4f1if3q7wzsi09dr8fv71qfagj8m25zrh3lvj";
View config
# Put in your ~/.ssh/config
### Problem
# You are on a public WiFi network that blocks SSH ports but you don't want to switch
# to pushing your Github changes to GH remotes via HTTPS nor do you want to change the
# remote hostname in all your repos.
View FooBarBaz.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE RankNTypes #-}
module FooBarBaz.Main
( compose3a
, compose3b
, compose3c
, main
) where
View cors-validation
#!/usr/bin/env bash
function usage() {
>&2 echo "Usage: $0 SOURCE_URL PROXY_URL"
function main() {
local -r source_url="${1}"
local -r proxy_url="${2}"
curl \
View .gitignore
View church-numerals.scala
SP: currying this gives the type: (T => T) => T => T
That looks an awful lot like Haskell's application ($) operator. :)
There is a higher order abstraction here, sniff it out.
type ChurchNumeral[T] = (T => T, T) => T
def cn0[T](f:T=>T, x:T):T = x
def cn1[T](f:T=>T, x:T):T = f(x)
def cn2[T](f:T=>T, x:T):T = f(f(x))
$ ruby bench/lazy.rb
                                                                                                           user     system      total        real
using Range#lazy                                                                                       0.330000   0.000000   0.330000 (  0.330689)
using Enumerator#yield                                                                                 0.170000   0.000000   0.170000 (  0.177117)
using Fiber#yield                                                                                      0.340000   0.120000   0.460000 (  0.458015)