[Nix-dev] Re: [Nix-commits] SVN commit: nix - 17736 - NicolasPierron - nixpkgs/trunk/pkgs/lib
Nicolas Pierron
nicolas.b.pierron at gmail.com
Fri Oct 9 20:48:32 CEST 2009
Hi Nixers,
I'll give you more details about this patch and why it is necessary ;)
On Fri, Oct 9, 2009 at 20:11, Nicolas Pierron
<nicolas.b.pierron at gmail.com> wrote:
> Replace a counter intuitive behaviour of module evaluations.
>
> - types.nix:
> Introduce a new flag named "delayProperties" which define either that
> properties should be evaluated (when false) or that they should be delaied
> through the type structure.
>
> - properties.nix:
> Generalized the delayProperties function to make it work with the iter
> functions of option types.
>
> - modules.nix:
> Replace evalProperties by a condition based on the value of the
> "delayProperties" flag of the option type. If the flag does not exists or
> if it is false, then the system behaves as always. Otherwise it delays
> the properties from the current value to each values contained inside it.
This patch is used to fix a strange behavior which in fact is a feature.
Properties are made to be delayed until you reach an option
declaration. So let's say you have 3 modules which are all defining
the same option, but they all have different properties:
# module 1
{ config.foo.bar = [1]; }
# module 2
{ config = mkIf config.baz.enable {
foo.bar = [2];
};
}
# module 3
{ config = mkDefaultValue {
foo.bar = [3];
};
}
At this time it's pretty easy to determine what would be the value of
"config.foo.bar". You'll never get the value of the module 3 because
the module 1 override its value, and you may get the value of the
module 2 in addition to the value of the module 1. Recal: mkIf and
mkDefaultValue are properties. Properties are made to be delayed, and
that's what happens to the mkIf statement. When the option "foo.bar"
is evaluated, the system evaluates the following list:
[ [1] (mkIf config.baz.enable [2]) (mkDefaultValue [3]) ]
The properties are evaluated before you enter the check function of
the option because you don't want to check non-used values.
Now let's describe the problem: We declare the foo.bar option to
accept attribute sets with embedded modules (sub-modules).
# module 0': declare foo.bar
{ options.foo.bar = mkOption {
default = {};
type = types.attrOf types.OptionSet;
description = "...";
options = {
value = mkOption { .. };
};
};
}
In this case, the sub-modules of "foo.bar" contains one option
declaration. The advantage of using such system is that you can
define multiple times the same entity, as you can do with modules. So
let's adapt the previous example:
# module 1': define test1
{ config.foo.bar.test1.value = 1; }
# module 2': define test2
{ config = mkIf config.baz.enable {
foo.bar.test2.value = 2;
};
}
# module 3': define test2 !
{ config = mkDefaultValue {
foo.bar.test2.value = 3;
};
}
So base on what I told you on the previous example, we should obtain
the following list when evaluating the option "foo.bar" which has been
declared in the module 0'.
[ { test1.value = 1; }
(mkIf config.baz.enable { test2.value = 2; })
(mkDefaultValue { test2.value = 3; })
]
Here comes the counter intuitive problem. What should be the default value:
3 or { test2.value = 3; } ?
Previously, when config.baz.enable is false, we got the following result:
config.foo.bar == {
{ test1.value = 1; }
}
Indeed, there is no test2 inside it because there is a mkDefaultValue
around it. You cannot guess it when you see the difference between
the original modules and the primed modules.
So, this patch is used to delay the evaluation of properties in such
cases, which means that you will have the test2.value attribute which
will be set to either 2 or 3.
One bad point is that you cannot override the whole list to re-define
it entirely, but this is not a big lost because we do not want to do
such things with these kind of options.
--
Nicolas Pierron
http://www.linkedin.com/in/nicolasbpierron
Andrew S. Tanenbaum - Never underestimate the bandwith of a wagon full of tapes.
More information about the nix-dev
mailing list