11 ms·
> Using Haskell as a base for the syntax I don’t really think this is a problem at all? I mean, at the particular design point the language occupies there are
by mananaysiempre 5y ago
> Using Haskell as a base for the syntax
I don’t really think this is a problem at all? I mean, at the particular design point the language occupies there are basically three possibilities:
- Kind of like Lisp (parens everywhere, painful with pervasive currying);
- Kind of like ML (var and fun everywhere);
- Kind of like Haskell.
Nix is chooses the last of those, and it does not have most of Haskell’s funkiness: no “alignment”-based blocks, no custom operators, no separate type sublanguage. The merged string interpolation / field lookup thing looks kind of funny, but has nothing to do with Haskell. The scoping rules for 'with' are weird, but, first, are not syntax, and second, have nothing to do with Haskell either.
Really, if one were to design the language that fits Nix’s description—a dynamically-typed (not Dhall), lazy (not Guix) higher-order (not CUE) language with a JSON data model—I don’t see how it would look much different. It could be less consistent, it could have more infix operators for things, or it could use a list concatenation operator that is not like anything instead of like Haskell. That’s it? If anything, any alternatives I can think of would be less conventional and/or more Haskell-like.
- KronisLV 5y ago> I don’t really think this is a problem at all? I mean, at the particular design point the language occupies there are basically three possibilities: How so? Where's the Java/Python-like languages in this consideration, why wouldn't those work? Or something more like Ansible? I'm pretty sure that you can express the same concepts for the most part in something imperative as well.
- colordrops 5y agoI don't see how you could maintain purity and reproduceability with something imperative. I used ansible for a while and it was a mess that broke half the time and was difficult to maintain.
- tracnar 5y agoDoesn't Bazel do exactly that with their Starlark language based on Python? https://bazel.build/rules/language#mutability https://bazel.build/rules/language#mutability
- pharmakom 5y agoKey aspects of Bazel are not imperative. For example the “load” functions commands must not be computed.
- tjoff 5y agoHow does that have anything to do with it being imperative and not the fundamental design of the system? How nix is architectured is exactly why it achieves purity and reproducibility, not the configuration language. It isn't even a goal of ansible in the same sense, per design. The building blocks of nix is simple, of course it can be implemented in something imperative.
- xena 5y agoThink about it like this: with Ansible you start from some unknown state of the system and then iteratively do actions to hopefully get what you want. With Nix you know what the base state of the system is, and the configuration language lets you describe your desired end state. Then the computer figures out what to do in the middle. The fundamental design of Ansible is that you do imperative steps to reach an end state. The fundamental design of NixOS is that you describe the end state and the compiler derives the intermediate steps. Yes, normally things work out with Ansible, but the devil is in the details and can involve a lot of fighting configurations that are non-cromulent.
- tjoff 5y agoYes - that was exactly my point. It has nothing to do with the configuration language. The end result of nix(os) could just as well be done from a bash script (not advisable, no). And the problems with ansible would not be any better off if they had used something like the nix-language instead.
- xena 5y agoFun fact: Nix is really a compiler from a weird Haskell like DSL to bash!
- sciurus 5y ago> The fundamental design of Ansible is that you do imperative steps to reach an end state. No, the fundamental design of configuration management systems (Ansible, Puppet, Chef, Salt, CFEngine) is that they are declarative. You say "this software should be installed, this user should exist, these files should have these contents and permissions, etc" and they figure out the right steps to take on your OS to move from the current state to your desired end state. You are right though that they can produce unexpected results since they do not describe the entire system from the ground up.
- deltaonefour 5y agoYou can. So long as the impurity is scoped to be local within a package. And the system itself only reads the output of your language to configure something. Example: def packageInstaller(pkgs: Pkg) -> Derivation: x = { req: [pkgs.a, pkgs.b] build: ... } x[req].append(pkgs.c) return buildDerivation(x) packageInstaller outputs a single package description, but you can also create something similar that easily outputs an entire configuration for the OS. You just need to keep mutation scoped within the language. The configuration that is outputted is immutable and whatever thing that's processing that configuration is not within user control. Keep in mind that mutability and immutability share a certain isomorphism. Within the nix expression language you often do something similar to mutation just as any other functional language. For example: listVersion3 = listVersion1 ++ AdditionalStuff It's just that in functional languages you're forced to keep versions of each mutation.
- cwp 5y agoYes and no. Yes, because Turing completeness. But no, because if you tried to do something like the Nix package manager with a language like Python or Java or even Scheme (yeah, I'm looking at you Guix), you'd end up with a worse system. What's great about Nix the language is that its design matches beautifully with its purpose. Nixpkgs is a single giant program, the output of which is a set of 70,000 package definitions. Since Nix uses lazy evaluation, you can specify one of the attributes of that set, a single package, and the Nix interpreter will evaluate just that one package expression. Of course, if that package depends on other packages, those will get evaluated as well, because they're needed as inputs to the package you care about. So package dependencies emerge naturally as computational dependencies between function invocations. This is really powerful, and over the years, Nixpkgs has evolved more and more sophisticated ways of defining packages - package overrides, the overlay system, and most recently, flakes. So yes, using an imperative language would technically be possible, but I doubt that you could use it to build the largest and freshest package repository with a fraction of the contributors that other packaging systems have.
- emteycz 5y agoWell said. Using imperative language would be possible but then it'd look like a bunch of function invocations, not like a class declaration. And people would be asking "this is crazy, why don't you create a DSL for this? My IDE can't ever understand this".
- tjoff 5y agoI really struggle with selling argument of lazy evaluation in the nix context, it is not a big deal. Sure, nix has been built around it, but it could just as well have been built around something else. And most certainly can't see how that would influence the number of packages. And honestly, the package situation is the biggest downfall of nix(os), there are tons of packages, yes. But they are often buggy (simply because nix is so different), not maintained (understandable, people move on), or plainly missing (because the few maintainers don't cover even popular niches). Of course that is going to be the case for a new system, but I'm flabbergasted every time it is being touted as an advantage of nix(os). And I feel that it is quite counter-productive tricking people into the ecosystem and giving the impression that it is on par or even better than any mainstream OS. It is making great progress, but it has a long way to go.
- yakak 5y ago> Or something more like Ansible? This seems to be the argument that comes up over and over with go, JavaScript, Ansible, etc. Sure one can take an imperative language and try to follow FP principles to emulate good things. Then one is comfortable with how it works while not really understanding FP and when it trashes the system by being implemented as a sequence of trashes of the state it is just a learning experience of user error.
- ris 5y ago> Or something more like Ansible? If you make it more like Ansible, you will end up with ... Ansible. If you want to use Ansible you are perfectly welcome to, but as a Nix person the entire Ansible way looks like a completely wrongheaded approach to system management.
- sshine 5y agoIf manual Linux server maintenance is like working with clay, Ansible is like a clay gun. It works great when you have to initiate work on a server you have limited control over; e.g. one that is provisioned from a cloud provider. And where I don’t need more than a few commands, and never need to replay them. Ansible is “bottom-up” (you start in the middle of a server’s lifetime without knowing everything); many more cloudy solutions like Kubernetes assume that a bunch of infrastructure is already set up; container registries, build servers, and the control plane. My first Ansible use-case was to install K3s on a dedicated machine where I could only choose the OS image from a few options like Debian and CentOS.
- mananaysiempre 5y agoYou could make a package manager or distribution along the principles of Nix with any reasonable language, of course. Insisting on first-order data (i.e. not functions) exclusively would probably have the standard problems of trying to cram into a declarative configuration something that really wants to be a program (“YAML ops”, programs-as-SQL-tables disasters like Windows Installer, XML hells like Ant and XSLT, etc.); but once you go all the way to a general programming language, sure. I would probably prefer Lua to Python because while Python can sort of do declarative EDSLs they invariably suck, and the expressive power and general style of the two is largely similar; Java has all those problems times a hundred so I expect you would end up staging the whole thing into a config processor in Java and a config in something else; but while the ergonomics may vary it’s certainly not impossible. Guix, Pacman with Aconfmgr, or the venerable GNU Stow are all this to some degree. That is not what I was saying, though. The semantics of the Nix language (JSON-like data model, lazy, higher-order, dynamic types) are a vital part of the implementation approach taken by the Nix package manager, and once those semantics are fixed the syntax (so I argued) has to be broadly similar to what it is now. (You could force a Java- or Python-like syntax upon an ML- or Scheme-like language if you really wanted, but the result would just be miserable to program in; the F# people tried but mostly gave up.) Is that approach the right one? I suspect so, but it is still to early in the life of the whole idea to be really sure. The idea that a dynamically-typed lazy (thus pure) higher-order language is a good middle ground between a completely declarative configuration and a completely imperative setup script (neither of which can usually stand alone) is a fresh and interesting one, and I’d very much like to see it explored. I’ll be the first to admit that the documentation on Nix internals is lacking, but once the requisite source spelunking is done it’s delightful how the whole thing comes together and how much flexibility being written in itself affords it. (Did you know that NixOS was originally a fun experimental addon to the whole thing? The packaging language accidentally ended up being powerful enough to make a usable and novel system configuration language.) But the complaint seemed to be that the syntax (which is relatively superficial and does not require particularly deep argumentation) is Haskell-like, not that the implementation approach (which is crucial to the whole design and requires arguments on the scale of a research paper) is wrong. My point was that the latter pretty much implies the former.
- 5y ago
- soraminazuki 5y agoThe Nix language's main concern is handling large scale configuration. General purpose programming and scripting is completely out of scope. The Nixpkgs repository has over 80,000 packages [1] contained in a single key-value store. The design of the Nix language not only makes this sustainable, but even compelling. For one thing, Nix package descriptions are composable and easy to manipulate programmatically. It's common practice to create new packages on the fly out of existing package definitions, and it's what the Nix language is particularly good at. Things like this add a lot of complex inter-dependencies within the Nixpkgs code base, and it still manages to be comprehensible. The same approach isn't practical with general purpose imperative programming languages. A quick glance at Nixpkgs repository reveals that it contains over 2.4 million lines of Nix code. Imagine that much Java/Python code manipulating a single global mutable data structure. It can only result in a nightmare. Additionally, the notion that the Nix is Haskell and is therefore incomprehensible is an often repeated meme without any backing details and needs to stop. If any language is similar to Nix, it's Jsonnet. But I haven't seen a single person criticizing Jsonnet [2] for being Haskell. If you can understand JSON and have no problem writing functions for any common language, then there really should be no problem learning the Nix language. [1]: https://search.nixos.org/packages https://search.nixos.org/packages [2]: https://jsonnet.org https://jsonnet.org
- danmur 5y agoI love many aspects of Nix but for me at least the language is a barrier. I spend time and get better at it but there's a fine number of things people can spend that time on.
- 0des 5y agoJSON would be a nice way to make a config file. I say this as an xmonad user too, which does its configs in Haskell as well. I love Haskell its just I don't want it to be my config language
- stavros 5y agoI don't think JSON is a nice way to write config files at all. You can't even add comments. I'd much prefer something like Cue.
- xena 5y agoI think the hard part here is using enough of a type system to ensure data sanity but not so much of one that you drown in metadata. I'm not really sure of a proper compromise like that.
- stavros 5y agoYeah, fair point. I don't know Cue well enough to know how much drowning there is, from my limited understanding you can specify as many types as you want and leave the rest dynamic, but I'm not sure at all.
- dotancohen 5y agoSo long as the parser doesn't except on undefined keys, you can add pseudo-comments to many places in a JSON config file. { "foo": "bar", "COMMENT": "This bars the foo" }
- ithkuil 5y ago
- OJFord 5y agoHow about HCL? (Hashicorp Configuration Language, as used in Terraform for example.) It's not really 'kind of like' any of Lisp/ML/Haskell IMO, and a good fit I think. Maybe it wouldn't allow everything or easily, but then if we're already only talking about being 'kind of like' (not actually using) an existing language that's not a problem. (It's more like Prolog I suppose, not syntactically but in declarativeness, which is also missing from your list. Unless it's lumped in with Lisp for having too many parens.)
- mananaysiempre 5y agoNo particular problem with parens, hell if you want to do postfix I’m cool with that too, it’s just that the leading parens really discourage you from currying by default while ML-style juxtaposition-is-application does not (normally ML and F# curry a bit less, Haskell and Scala a bit more, but both curry much more than is usual in Common Lisp or Scheme). And currying is really useful when some of the curried functions are values of config settings. I didn’t think HCL had laziness (which is vital to how NixOS module or Nixpkgs overlay A can set or use things in B which in turn set or use things in A), I’ll have to look at the theory more closely, thanks. (Its documentation tries pretty hard to make HCL look boring and weak, I guess it’s my fault for believing it.) Doing logic-programming-like things would probably also work as an approach to achieving these goals, yes, just a different one from that implied by my constraints above. I’d count CUE as being interesting in that direction as well even if it’s not technically logic programming (is it?.. the bibliography is not great). I’ll guess we’ll see.
- lelanthran 5y ago>> Using Haskell as a base for the syntax > I don’t really think this is a problem at all? Of course it's a problem. You shouldn't use a syntax that has been soundly rejected by the industry.
- rowanG077 5y agoBecause industry makes good decisions? Decisions like adopting python, dooming a large part of the world software to be bug ridden slow messes. Or to elevate CUDA to the facto machine learning backend? Besides it's not clear that Haskell isn't adopted by industry due to the syntax.
- lelanthran 5y ago> Because industry makes good decisions? That's irrelevant - if you want uptake, you need to support what user's want. It's been clear for at least a decade now that programmers don't want Haskell syntax. Whether it's a good syntax or not is irrelevant.
- rowanG077 5y agoWhy do you think it's Haskells syntax that is the problem? The syntax is not so weird. I'd say the semantics are the hangup. 100% purity and lazy evaluation are a much harder pill to swallow then the syntax.
- lelanthran 5y ago> Why do you think it's Haskells syntax that is the problem? The syntax is not so weird. I'd say the semantics are the hangup. 100% purity and lazy evaluation are a much harder pill to swallow then the syntax. Could very well be - either way the language has been rejected. Boasting of Haskell characteristics[1] is, IMO, a great way to turn off prospective users. [1] I don't really know if the Nix project actually does blog posts and reachouts where they boast of Haskell-ish similarities.
- deltaonefour 5y agoIf they picked javascript I'm sure nix would be more popular, but also despised by the same crowd that currently loves it.
- rekado 5y ago> lazy (not Guix) This is such a weird thing that keeps coming up. Why would the language have to be lazy? Obviously, there are tons of things that are lazily evaluated in Guix, e.g. the whole package graph. Why would Scheme have to be a lazy-by-default language? It baffles me that this objection keeps ending up on people's lists.