79 lines
2.7 KiB
Markdown
79 lines
2.7 KiB
Markdown
---
|
|
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.
|
|
|
|
The next step is making a function that has access to the unmodified original
|
|
values. This is managed through the `extends` function. It took a while for me to understand what's happening in there, but luckily nix has [good documentation](https://github.com/NixOS/nixpkgs/blob/67b1265fb3d38ead5a57fee838405a2d997777c2/lib/fixed-points.nix#L37-L65) for it.
|
|
|
|
``` nix
|
|
extends = f: rattrs: self: let super = rattrs self; in super // f self super
|
|
```
|
|
|
|
- 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
|