One use case for lazy evaluation in NixOS is that you can reference other parts of the system configuration, possibly even defined in other modules, as long as you don't produce cyclic references. For example, I use stuff like the following often to avoid accidentally specifying a username that does not exist:
Another use case is being able to keep everything as plain values. For example, I can run `import <nixpkgs> { }` without evaluating all of `nixpkgs`. You can accomplish that in a language with eager evaluation by wrapping values in functions but I prefer the everything-is-a-value way.
Of course, this is just a matter of preference. Like most Guix vs. Nix aspects, as far as I can tell.
The "service" mechanism in Guix System is designed in part as a reaction against those the NixOS module design you're describing: I think the ambient authority, free-style extension mechanism of NixOS modules makes it hard to reason about it (any module can change the config of any other module), and it makes it easy to shoot oneself in the foot (with infinite recursion in particular, as you write).
In Guix System, services extend one another, forming a directed acyclic graph of extensions. When a service extends another service, that connection is explicitly declared, there's no surprise.
Nix bombs out when it runs into a cyclic reference, so at least infinite recursion doesn't hang without any output. Likewise, it also rejects multiple definitions for a value (though to be fair some values like attribute sets are mergeable and it can be hard to wrap one's head around that).
The service model in Guix looks pretty neat! I've long been thinking it would be nice to have a configuration system for NixOS that uses isolated components with a defined (type-safe) interface. The current modules are kind of that, except it all gets squashed down into a single nested attribute set, so it's not really isolated and (understandably) can be confusing. How does Guix System handle multiple services competing for the same global resource (port allocation, file in /etc, ...)?
There's nothing to prevent multiple services from using the same port at this stage. For files in /etc, the "etc" service should detect that the same file appears more than once and error out--that is, it won't instantiate a "broken" system.
There are other things that are detected statically and prevent you from instantiating a broken system: missing modules in the initrd, invalid file system label/UUID, references to non-existent Shepherd services, etc. All this is possible because there are well-defined data types and semantics for the different components of the system.
Fair enough, NixOS doesn't completely solve this either (other than the aforementioned possibility to reference other parts of the system config and some integrity checks). I was just wondering if that's an area where more static checks are possible.
That sounds pretty good though, thanks! Maybe I'll try out Guix System some day.
Of course, this is just a matter of preference. Like most Guix vs. Nix aspects, as far as I can tell.