On Mon, Dec 22, 2008 at 16:02, Ludovic Courtès <[email protected]> wrote: > In good faith I was trying to write an Upstart job for PulseAudio using > this new style, but I'm afraid it's beyond my capabilities...
I don't think so ;) So I will try to explain it with more details. > "Nicolas Pierron" > <[email protected]> writes: > >> The implementation need all other module used to implement it and the >> options defined by this module with the attribute named "require" > > I don't understand this sentence. Me too, my fault. The "require" attribute is used to list all configuration files that you need to implement your own configuration file safely. The idea is that it imports in your own attribute set all configuration that are needed for your implementation. >> require = [ >> # options needed for the implementation >> (import ../upstart-jobs/default.nix) > > This happens to be commented out, e.g., in `upstart-jobs/cron.nix' in > `trunk'. Why would it be needed? In the trunk, "../upstart-jobs/default.nix" is not yet a valid configuration file, therefore this would create a failure. >> # options defined by this module >> options >> ]; > > Interestingly, options defined by this module are *provided*, not > *required*, so why does one need to list them in the `require' > attribute?! This is more like a legacy from the first time I got this idea, which is to import sub-configuration files. > Furthermore, how does that `require' magic work? It appears that > `fixOptionSetsFun' (from `lib/default.nix' in Nixpkgs) plays an > important role, but I don't understand it, and the comment above isn't > very helpful either to me. Usually a magician does not reveal his tricks, but I am not one ;) The concept is quite simple, I will explain it by describing each function in detail and their role in the problem. uniqFlatten: This function takes a list "x" and flatten it with the function "prop.next", at the same time it skips elements which have the same key (prop.key x) and it returns the list of values (prop.value x). This function handle the "require" attribute inside the "prop.next" which retrieves the content of this attribute. Elements retrieved from the "require" attribute are candidate to be added in the list if there no elements with the same "key" is already inside the returned list. This function handle the "require" attribute to create a list of imported elements without duplication. This function does the same as a text closure except that you have keys which are using a side effect of the Nix evaluator. Nix a lazy language with maximal sharing. Both features are used here. The laziness is used to skip the evaluation of value and the maximal sharing is used to compare the elements returned by the imports statements (this is a side effect of the evaluator). Thus uniqFlatten is able to remove duplicated imports by comparing functions & sets implementations. zip: This simple function is used to merge all attribute (configuration) sets into one big configuration set. mergeOptionSets & filterOptionSets: These functions are used to merge all configuration sets together with the zip function. When they reach an attribute which has an option definition (result of the function "partition isOption opts") then it will look into it to know how to merge all elements of the list "opts" which are not option definitions ( test.wrong == filter (x: ! (isOption x) opts ). test.right == [] -> no option defined => recursion to merge deepest options. test.right != [] -> at least one option is defined. | test.wrong == [] -> no values are defined, use the default value. | test.wrong != [] -> values are defined, use the merge function of the option. fixOptionSetsFun: This function takes the computed configuration (either computed by fix or one already well defined) and use uniqFlatten to fetch all configuration sets and merge them with mergeOptionSets or filterOptionSets. mkIf & mkThenElse & mkAlways & *If: These functions are used to factor parts of the code that looks like { foo = if config.qux.enable then .. else ..; bar = if config.qux.enable then [..] else []; // most common case. (else empty [..]) baz = [..]; } by mkIf config.qux.enable { foo = mkThenElse { thenPart = ..; elsePart = ..; }; bar = [..]; // computed by evalIf baz = mkAlways [..]; } The important point is where the condition is put could lead to an infinite recursion caused by the evaluation of the config attribute. So the only possible solution is to use this "mkIf" which allow to delay the evaluation of the condition until it is evaluated by the merge function. delayIf: It is used to move the mkIf statement deeper inside the attribute set. This function is used during the traversal made by mergeOptionSets & FilterOptionSets. evalIf: It is used to evaluate the condition before the merging of the option values. rmIf: It is used to ignore a condition. Its main usage is inside fixOptionSetsFun to access the "require" attribute without specifying any mkAlways around it. So you cannot do conditional import (with mkIf) has you need to import all files in order to evaluate the condition. I hope these descriptions helps you to get a better understanding of the system. -- Nicolas Pierron http://www.linkedin.com/in/nicolasbpierron - If you are doing something twice then you should try to do it once. _______________________________________________ nix-dev mailing list [email protected] https://mail.cs.uu.nl/mailman/listinfo/nix-dev
