diff --git a/posts/configs.md b/posts/configs.md new file mode 100644 index 0000000..1cab023 --- /dev/null +++ b/posts/configs.md @@ -0,0 +1,71 @@ +--- +title: Extensible configuration Pt. 1 +date: 2019-03-27 +--- + +This is the first part of a series where I'm going through how to make +extensible configuration. There is nothing groundbreaking or new in this +series, it's just me going through different implementations and trying to +understand them. + +The source material for this post is [the fixed point implementation](https://github.com/NixOS/nixpkgs/blob/master/lib/fixed-points.nix) for nix. + +By extensible configuration, I'm talking about nix style extensible +configuration, like overlays, overrides and extensions. Let's see an example of +an extensible configuration. + +``` nix +{ haskellPackages, fetchFromGitHub }: + +let + purescript = fetchFromGitHub { + owner = "purescript"; + repo = "purescript"; + rev = "2cb4a6496052db726e099539be682b87585af494"; + sha256 = "1v4gs08xnqgym6jj3drkzbic7ln3hfmflpbpij3qzwxsmqd2abr7"; + } + hp = haskellPackages.extend (self: super: { + purescript = super.callCabal2nix "purescript" purescript {}; + }); + +in + +hp.purescript; +``` + +On a high level we are augmenting the `haskellPackages` attrset by replacing +the existing purescript package with a different one. The extension is a +function that takes two arguments, `self` and `super`. `super` is the original +non-lazy value and `self` is the lazy value that corresponds to the value at +end. + +The first step on this journey is done by getting to know `fix`. Fix is +described being the least fixed point of a function. In practice it's a +function allowing declaring recursive functions without explicit recursion. + +``` nix +fix = f: let x = f x; in x +``` + +With fix you can have access to the lazy `self` value. It's value is whatever +would have been computed in the end. As it is lazy, it is possible to end up in +a recursive loop if there is a cyclic dependency. + +``` nix +let recursive = fix (self: { + foo = 3; + bar = self.foo + 1; +}); +infinite = fix (self: { + foo = self.bar + 1; + bar = self.foo + 1; +}); +``` + +You can try those yourself. The first version is fine and returns an attrset +like you would expect. The second one has a cyclic dependency and nix helpfully +errors out. + +- https://elvishjerricco.github.io/2017/04/01/nix-style-configs-in-haskell.html +- https://github.com/NixOS/nixpkgs/blob/master/lib/fixed-points.nix +- https://chshersh.github.io/posts/2019-03-25-comonadic-builders