There are actually much less rules than "normal" programming languages. I had started to write my own basic functions that would be in a "standard library" (trim(), trunc(), remove() etc) and got the hang of it after a couple hours.
The only thing I don't like about the language for "serious" work on massive projects is missing static types (may make it less readable though..), and the tooling (most people lean on Emacs) could be better.
So I'm guessing there's some additional conventions for function naming, besides appending ? to the name for boolean return and ! for functions that mutate.
[1]https://spritely.institute/static/papers/scheme-primer.html
It can't be a yaml file because it's essentially a program, it's a lot easier to write such things as a program.
Certain sections of nix files have special meaning (config), certain functions only work in those sections. Nice features like `//` for merging dicts goes out of the window as soon as you have nested dict, which is nearly every time.
It's poorly documented, “standard library” documentation is even worse. It's impossible to navigate because everything is mashed together in `nixpkgs`. Often show no examples on how to use a function and just say `it takes blahblah`, but what is blahblah?
Ah, so Nix needs lenses.
Edit: On the NixOS matrix I was told "there is lib.recursiveUpdate" when I mentioned your comment.
Does that fix this problem for you or improve things?
That's what I'm bitching about - discovering documentation sucks because nix-lang is a very niche language and `nixpkgs.lib` is mostly undocumented. That makes finding how to do something in it is harder compared if to any other language.
With homebrew, it's just ruby scripts. You write ruby, you can ask some ruby question from someone who never wrote a formula and get an answer because that's a general purpose language.
With guix, it's not the best, but it's still just GNU Guile, it's been around for almost 30 years. Yeah, it's probably not very popular, but it's still a general purpose language that has uses outside Guix.
Keeping this in mind, I find the Guile scheme syntax used by Guix quite intuitive and pleasant. It reduces the cognitive overhead to configure the system, at least for me.
Also each configuration file in Linux has already has their differences and quirks
- the module paths a bit weird (I’d find slash or dot separated more obvious)
- some values needed to be constructed with a call to list
- weird dsl for building was mixed in there
- it’s probably not obvious what should be string/symbol/whatever
I’m not sure anything should change though. It still feels a bit weird to me that it’s a program generating the package rather than a more direct ‘just data’ format.
Oh, and how can a package manager avoid dealing with build systems? There are almost as many different build systems as there are packages!
I continue to find it slightly odd that the package definition is not some serialisation of a package object but rather a full program that outputs a package object, and I think this is the root of a bunch of the confusing things about the definition. Consider that the commenter at the top of this thread thought yaml might be preferable. I think that means they didn’t realise they were looking at a program outputting the package definition but rather that they thought they were just looking at the definition itself.
If the whole thing were just data instead of data that’s actually code whose evaluation produces data, I think things would be more obvious. For example, some symbols are just syntax (e.g. in the alist syntax for constructing the package definition) whereas others are references to values of which some are other packages, some are just scheme functions, and some are guix-specific functions. And symbols have other meanings in the build-steps reader macro.
Building package definitions in this way can also make errors harder to pinpoint as the author can write any program they like rather than being restricted to a more limited configuration language. Needing to evaluate programs to get packages can also, I think, make the collection of all packages as a whole pretty opaque and hard to reason about compared to e.g. being able to have a database that contains all the package definitions.
I don’t think the ‘unfamiliar with lisp’ argument is good. I’m pretty familiar with lisp (though not so much guile/scheme) and I’ve been writing it for over ten years and I think this way of specifying packages is not great in the average case, though perhaps there are some complexities that cannot be handled without this general mechanism.
As for error messages, I don’t think that using a real programming language forces the error messages to be bad, or to be too generic. If the error messages are not good it is only because nobody has put in the time and elbow grease to make them good, not because of any fundamental limitation of turing–complete languages.
There's no special syntax for any of those dot slash whatever stuff. (Program item) paths are always lists (like in CSS selectors). Likewise we don't use shell strings and then have to weirdly escape them, we just use an argv list to begin with. And so on. What's the use in pretending it's something it's not?
>- some values needed to be constructed with a call to list
It depends on how good you are with quoting; if you want to avoid quasiquote, "list" is easier to understand. Otherwise, (almost) no values need to be constructed with a call to "list".
(list 1 2 3) is equal to `(1 2 3)
(list 1 2 b) is equal to `(1 2 ,b)
(list 1 'b b) is equal to `(1 b ,b)
>- weird dsl for building was mixed in thereIf you mean what I think you mean then it's a program running in the build container (in an extra process) (so it's quoted in the scm file of guix).
>- it’s probably not obvious what should be string/symbol/whatever
>I’m not sure anything should change though. It still feels a bit weird to me that it’s a program generating the package rather than a more direct ‘just data’ format.
It will always end up like that, even in packaging systems that start with a "just data" config format. They will always grow their own weird language for #if, #cfg, function calls and so on in the config format. Then they have weird generator scripts generating the config files, and there they are right where we are, just shittier.
I do actually prefer s-expressions to JSON, but only when used as pure data. Once you turn it into scripting language it gets ugly fast.
Which is why a good text editor will balance parentheses automatically and highlight macros. That being said, it is well known that macros can be abused and overused. It is considered bad style to use macros if a function would do the job too.
*Edit:* Guix supports JSON specifications too, though you are of course not as flexible as with Scheme: https://guix.gnu.org/manual/en/html_node/Invoking-guix-packa...
[
{
"name": "myhello",
"version": "2.10",
"source": "mirror://gnu/hello/hello-2.10.tar.gz",
"build-system": "gnu",
"arguments": {
"tests?": false
}
"home-page": "https://www.gnu.org/software/hello/",
"synopsis": "Hello, GNU world: An example GNU package",
"description": "GNU Hello prints a greeting.",
"license": "GPL-3.0+",
"native-inputs": ["gettext"]
},
{
"name": "greeter",
"version": "1.0",
"source": "https://example.com/greeter-1.0.tar.gz",
"build-system": "gnu",
"arguments": {
"test-target": "foo",
"parallel-build?": false,
},
"home-page": "https://example.com/",
"synopsis": "Greeter using GNU Hello",
"description": "This is a wrapper around GNU Hello.",
"license": "GPL-3.0+",
"inputs": ["myhello", "hello"]
}
]Which is what Guix is doing. Just look at (package), why isn't that a function taking an associated list? It's this kind of ad hoc DSL magic that makes Lisp-like languages incredible hard to read, since you never know what you are looking at.
Compare the package macro:
(package
(name "guile-sdl2")
(version "0.7.0")
(source (git-checkout (url (dirname (current-filename)))))
…)
With a package function that takes an alist: (package
`((name . "guile-sdl2")
(version . "0.7.0")
(source . ,(git-checkout (url (dirname (current-filename)))))
…)
Notice how the alist has to be backquoted so that you can put a comma in front of everything that needs to be evaluated, and how every entry in the alist has to be a pair rather than a list which requires a dot on every line. Which would you rather type out a hundred thousand times? Which would you prefer to teach to new contributors? Which would you prefer to answer questions about on a mailing list?Neither. But that proofs my point, Lisp is unreadable for the average user. If your first instinct is to not use the language itself, but write your own DSL on top of it to make it readable, maybe that language ain't such a good choice.
Plus, you can’t assume that using JSON or YAML will give your users some kind of magical intuition bonus; you’ll still have to train them to use JSON/YAML correctly. I have a friend (who isn’t a software engineer) who needed to do some work on JSON files, and it was pretty clear that he was just cargo–culting it. He was only doing it to accomplish some larger goal, not for the joy of knowing the JSON syntax. That’s a hurdle you will face no matter what you do.
JSON would at least work consistently and don't randomly redefine the meaning of the language midway through a config file. YAML numerous issues of its own, so I'd avoid that if possible.
This isn't a Guix specific problem, the GNU project has tried to make Guile a thing for the last 20 years or so, and it just never looked especially elegant to me in any context and never really gained any real adoption in the wild either.
And when it comes to package configuration, it's just an unnecessary issue, we already had Nix, which comes with it's own JSON-like language specifically build for this task and that is much nicer to work with than Scheme, because it's configuration language first, not a general purpose programming language turned into one via macros. Using Scheme instead just doesn't improve the situation in any way in my eyes, it just creates a lot of additional problems.
Tongue in cheek :)