-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Native module system #1505
Comments
I would like to make sure that I understand the semantics precisely. In your example, you already know that you need a
Or even:
It sounds like
In which case the situation is different, because then the contract annotation propagate to all subsequent merged values (while it doesn't for free-standing contract annotations, which don't have any notion or knowledge about merging). Otherwise, you might want to extend your record freely with new values, but then I'm not sure to get what is the criterion to distinguish between a "good extension" and a "bad accidental extension" that would differentiate this idea from just having an open record contract. |
To give you something closer to the “X“ of my XY problem, here's (very simplified) what we have right now:
This is pretty nice (though missing completion because of #1499), but the issue is that there's nothing to check that my final derivation will only use meaningful fields. I can write With my module system prototype, I can rewrite the above as:
Which is more verbose, but has the advantage of actually being able to complain if I type Maybe there's already a “native” way of doing what I want, but I couldn't figure out what it is. (I realise my example was confusing. I generally don't care that much about reusing values defined somewhere else, but I care a lot about defining values declared somewhere else) |
I just realize that I could partially fix that by using a contract application rather than merging when defining my derivation at the end ( EDIT: The probably doesn't have its place here. I did test it, and it does work. |
It seems that what you want, in the end, is to apply a contract to the fragment:
One solution, which I think is somehow what you do in NixOS modules, and what @vkleen does in tf-ncl, is to properly separate the interface of your module (whatever it means), that is the inputs, from the rest. One possible encoding, for example, could be to write:
I used In any case, in this example, because you're now merging
(or something like that - whatever, the idea is that adding an indirection level In the inputs/outputs example, I intentionally left the Maybe it could be an interesting exercise to see if you can define, for the different entities you have in nickel-nix (contracts, builders, derivations, environment definitions), a meaningful notion of contracts/inputs/outputs. I feel like the simple everything is a record + recursive merging in Nickel has a tendency to make us mix everything, leading maybe to some confusion. Everything that I wrote are mostly ideas or leads to follow, but I must admit we haven't figured out the whole story around "modules" yet. For now, we've stuck to not having a first-class notion, trying to get away with simple records, contracts and merging. But if this isn't sufficient, this will end up the same as in Nix: people having different encoding and implementation of a module system on top of core Nickel, and that's exactly what we want to avoid. In Eelco's gist on what's wrong about the Nix language, he says
The same could be said about Nickel: it's a DSL for configuration management, but it doesn't have a first-class notion of configuration (which would be modules, I think). If we can get away with not having more abstractions in Nickel, because there's a simple, efficient and canonical way to encode them in the current model, that's a huge win. But if not, we might consider having a proper notion of modules, interfaces and composition/extensions. |
The main thing I got from our discussion today is that you can accomplish something like what we want with the following (using "config" and "options" by analogy to NixOS options):
This gives you the right contract applications in the right places, but... problems with this approach include:
I was also thinking about it afterward and it strikes me that if we override
An example in nix package terms would be that for |
Hmm, it's true than in general, we don't have an easy way of expressing "at least one of those codependent fields must be set". But this is probably a bit more general that's the current issue, you might want to model that even in a standalone record contracts that doesn't have anything to do with modules. |
We discussed this yesterday with @yannham, @vkleen, @jneem and @Radvendii . Long-story short: There's no real first-class support, but we can have a cheap-ish encoding with something very similar to NixOS modules:
That doesn't play nicely with the LSP at the moment (for the completion at least), but there's a way forward (since it doesn't have to rely on custom contracts, just plain merging). Maybe there could be a more integrated way in the future, but it's not clear yet what it would look like. |
Just skimmed the discussion, but I'm a bit worried that an attempt to 1:1 port NixOS modules to Nickel will also reproduce their weaknesses. Nickel should be an opportunity to take a step back and find out which module system equivalent works the best. For example, the fact that one does not always need a Similarly, a lack of auto completion should not necessarily be a reason to change the language, if the LSP could be improved instead. |
I wholeheartedly agree with @piegamesde. I believe the goal is really not to port the NixOS module system, but to figure out a module system - or to figure out if we even need one - that should make NixOS-style modules reasonably simple to write. But not necessarily a 1:1 mapping, as it would be acceptable to abandon some Nix idiosyncrasies if it makes for a simpler and more universal system. I think it just happens that the need for modules is first arising in the work on Nickel-Nix, so it's a good driving use-case. But modules should be useful and adapted to Terraform, Kubernetes, OS configuration, etc. In general any Nickel language feature, while it can be motivated by the Nix use-case, should be universal, and not just a port of what exists in Nix (that's what we tried to do with symbolic strings, for example). |
Is your feature request related to a problem? Please describe.
The merging system of Nickel is awesome. It doesn't natively solve the exact use-case of NixOS modules because there's one thing that's missing: closed record contracts (only accepting the fields that are defined) that can be extended through merging, and that are only resolved at the very end (I remember some old discussion with @yannham where the system worked a bit like that, but the semantics changed in the meantime – globally for the better, and it doesn't anymore).
An example of what can be done with NixOS modules but doesn't contract-check in Nickel atm:
This is, however, incredibly useful when writing complex configurations as it allows them to be extended in a transparent way.
Describe the solution you'd like
Some nice way of expressing the above. I'm not sure what would be a good solution, I left some suggestions below
Describe alternatives you've considered
Encoded module system. I have a fun PoC here. That kinda works, but with the costs of an encoding (in particular, it can't work at all with the lsp, which makes it very sad).
Don't use closed contracts, but blindly accept everything in the records. Something like
That works and is simple, but since it relies on open record fields it will silently accept a mistyped field name.
Use open contracts like above, but find a way to filter out invalid fields after the fact. Maybe with some builtin that would tell which fields don't have a contract attached to error out on them
A different merging operation that merges the contracts and delays the contract checking. Feels very ad-hoc and error-prone a priori, but who knows
Additional context
Add any other context or screenshots about the feature request here.
The text was updated successfully, but these errors were encountered: