[Nix-dev] Overriding packages (was Re: Kernel headers)

Nicolas Pierron nicolas.b.pierron at gmail.com
Tue Aug 26 19:41:21 CEST 2008


On Fri, Aug 22, 2008 at 15:58, Andres Loeh <andres.loeh at googlemail.com> wrote:
>>> I brainstormed with Andres a while ago about how to make Nixpkgs more
>>> configurable without polluting it with zillions of options, which is related
>>> to this.
>>
>> Who is Andres ?
>
> That'd be me ...

Ok.

>
>>>    pkgsFun = pkgs:
>>>      { [..] } // (getConfig "overrides" {}) pkgs;
>>>
>>>    # Tie the knot.  This works thanks to laziness.
>>>    pkgs = pkgsFun pkgs;
>>
> finalReference = f: (rec { result = f result; }).result;
>
> The usual name for this is "fix", but yes, that's the same
> function. You can then say
>
> pkgs = fix pkgsFun;

You are right, I just forgot the naming of this famous function.

>> evalAttrDrvFun = name: attr: listToAttr [{
>>  name = name;
>>  value = attr.drvFun attr;
>> }];
>>
>> Therefore any reference to subversion attribute will refer to the
>> derivation as it is currently.
>
> I'm afraid anything involving listToAttr is too complicated for me.
> At least I don't understand this ...
>
>>> Maybe we need some language extension to allow attributes in recs to be
>>> overriden properly...
>>
>> I am not sure that creating directly a language extension would be a
>> good solution because someone will always arise with a new issue.  May
>> be we should concentrate our effort at findings the good solution with
>> the current syntax instead of searching new syntax that could help for
>> one specific kind of writing. [..]
>
> I'm also not sure, but ultimately, I'd rather like a more powerful
> merging operator built into the language and have all the reflection
> operators (converting attribute sets to lists and vice versa) removed
> instead.
>
> But I still think that we can do almost everything one could want
> with the current language and without reflection.

I do not agree.  Reflexivity can reduce redundancy and help to create
new evaluation schemes in order to express things in a clear way.

The reflexivity is one powerful feature that can be easily enable with
the laziness and which can provide more ""user-land"" syntactic sugar.
 I have one small example which is used by the option merger in
pkgs/lib/default.nix.  This example may represents a service (in
NixOS) which use another service only if it is already enable because
it is not necessary.

------- thisService.nix
config: pkgs:
{
  # extra definitions if the service is enable.
  services = if ! config.services.thisService.enable then {} else {
    requiredService = { enable = true; };
    # this test will raise a circular definition.
    extraService = if ! config.services.extraService.enable then {} else {
      option1 = true;
      option2 = false;
      ...
    };
  };
}

The second test verify that the "extraService" is enabled by the user
or really required by another service. It will raise a circular
evaluation because the condition evaluates an attribute which can be
defined inside the "then" or "else" part. To skip this problem we can
move the condition to each attribute definition.  Therefore the
circular evaluation can be caught and a function can try to spread the
if statement on the attributes before trying to evaluate the set
again.  Moving the if statement inside the set implies that we can
detect if statements and act on them.

Currently we can create statements by writing nix-expressions but it
could be much more interesting to match statements as well as
attribute names.  I understand that "listToAttr" is not user friendly.
May be we can replace this by a reflexive syntax that can manipulate
all the language and easily support language extension like:

spreadIfStatement = arg:

  # match a "if then else" statement
  match arg with  if cond then thenPart else elsePart  do

    match thenPart with
      # if the thenPart is an empty set
      {} do ...

      # if the thenPart is not an empty set.
    | { $name = value; } // thenNextPart do
        ( if elsePart ? name then
            { $name = if cond then thenPart.$name else elsePart.$name; }
          else
            { inherit (thenPart) $name; }
        ) // (spreadIfStatement (if cond then thenNextPart else elsePart));

This function does not support all cases but the idea is to add the
"match <expr> with <expr> do <expr> [ | <expr> do <expr> ]" statement
in the language to handle the reflexivity directly in the language.
The previous sample use another language extension which allow to
replace an attribute name by an attribute evaluation.

By the way, some primitive operation can be rewritten like this:

head = list: match list with [head] ++ tail do head;
tail = list: match list with [head] ++ tail do tail;

Even if this is not efficient this could become a good documentation
and the "match" statement can be make generic enough to support all
future language extensions.

-- 
Nicolas Pierron
- If you are doing something twice then you should try to do it once.



More information about the nix-dev mailing list