[Nix-dev] Re: NixOS: New scheme

Nicolas Pierron nicolas.b.pierron at gmail.com
Wed Nov 19 00:14:45 CET 2008


On Tue, Nov 18, 2008 at 22:49, Ludovic Courtès <ludo at gnu.org> wrote:
> Hi,
>
> Marc Weber <marco-oweber at gmx.de> writes:
>
>>> too coarse-grain, and it breaks the principle of least authority.  I
>>
>> In general this is good.
>
> No it's not.  :-)
>
>> I really fail to see what difference it makes as
>> - the jobs are run as root anyway
>> - the jobs can run rm -fr / as root as well..
>
> This is not what I had in mind.  I was referring to PoLA in the context
> of our "program" code, i.e., the Nix code in the NixOS repository.
>
> If we were to make an analogy with a language like C, passing `pkgs'
> and `config' to every function amounts to passing arguments using global
> variables.
>
> That is, instead of
>
>  int
>  do_things (pkg_t foo, pkg_t bar)
>  {
>    ...
>  }
>
> you would write:
>
>  struct
>  {
>    pkg_t foo; pkg_t bar; pkg_t baz; pkg_t chbouib; ...
>  } pkgs;
>
>  int
>  do_things (void)
>  {
>    /* Use `pkgs.foo', etc.  */
>    ...
>  }
>
> As a functional programmer, you will surely agree that this is bad
> programming style.  ;-)

Yes. Here is the current implementation analogy:

config_t system(set<pkgs_t> pkgs, ...) {
  ..;
  // unmaintainable function call & function arguments
  upstart_jobs(pkgs.foo, pkgs.bar, pkgs.baz, pkgs.qux, ...); // for
all packages used below
  ..;
}

list<jobs> upstart_jobs(pkgs_t foo, pkgs_t bar, pkgs_t baz, pkgs_t qux, ...) {
  job1(foo, bar);
  job42(foo, baz);
  job51(bar, qux);
}

So I don't think that this solution can easily be maintain as there is
lot of code to explain to handle it correctly.  Sure, if you succeed
to implement something with it, it would probably be more secure.  On
the other hand you will develop less feature due to this extra
complexity.

What I am suggesting with this writing is to have independent jobs
with an identical syntax (less complexity).  So every function will
become something like:

lazy<config_t> &do_something_well_defined(set<pkgs_t> pkgs,
lazy<config_t> &config) {
  // use packages related to the well defined operation.
}

Of course the pkgs and config attributes are some kind of catch all
set of attributes, but you will only use things related to your well
defined operation.  Moreover, as it is easier to read it is also
easier to review and fix.

> A practical consequence is that it makes it very hard to read the code
> and understand what a function does since it takes input data
> implicitly, from "the environment".

That is the reason why functions have names.

>  It also makes it impossible to
> "virtualize" functions: the second version of `do_things ()' will always
> use the global `pkgs.foo' and cannot be told to use something else as
> `foo'.

What do you mean by "virtualize" ?  You mean higher order function ?

>  In the context of the Nix interpreter, it probably prevents
> optimizations since it's as if every Upstart job depended on *all*
> packages (perhaps Eelco can comment on this).

If you prefer you can write something like:
{pkgs@{lib@{mkOption, ...}, foo, ...}, config, ...}:

-- 
Nicolas Pierron
http://www.linkedin.com/in/nicolasbpierron
- If you are doing something twice then you should try to do it once.



More information about the nix-dev mailing list