Typing Nix
tweag.io
tweag.io
Why they wouldn't take the most (syntactically) beautiful functional programming language out there — Standard ML? It would work perfectly for such a task. Or the second contender — Haskell. If C-like syntax is desired, the best contendant is probably Swift.
I cringe every time I have to edit a .nix file (and I have to do it a lot).
Neither SML nor Haskell are optimized for expressing deeply nested records with many string literals, for example. The multiline interpolated strings in Nix are extremely much better than in SML or Haskell. The way records and arrays are written is great: SML and Haskell both suffer from the tedious problem of using separators between items instead of after each one; in Nix each item can always be moved without messing with separators.
I think Nix is an engineering marvel up to and including the language design. If you can make a better surface syntax and demonstrate it by translating some significant part of Nixpkgs, I'd be very interested, but I think in general the language is the way it is because that's what made most sense for the system's designers.
Lisp is pretty good at lists.
The only important language is the derivation language sent to the daemon. Nix spends so much time building up inputs... for shell scripts. I always felt like they would have been so much more successful if they chose some other, more popular language to generate derivation.
http://git.savannah.gnu.org/cgit/guix.git/tree/gnu/packages/...
You'll see here that the package emacs-minimal actually inherits from the emacs package definition, and then all fields afterwards are essentially overwrites of whatever was in the emacs package.
Unfortunately, I just noticed that the manual alludes to this, but actually never explicitly documents it in the "Defining Packages" section :/
Purescript's extensible row types handle this process elegantly and if Nix takes a cue from that, it will work great.
let
foo = 42;
bar = "a string";
in {
baz = "deja";
quux = "vu";
}
Javascript: const foo = 42;
const bar = "a string";
return {
baz: "similar",
quux: "but different"
};
You find this kind of thing all over the place. Nix is a much smaller and simpler language syntactically, and more pleasant to write.I don't have extensive ML or Haskell experience, coming from Ruby/Java background.
I think the Nix language works great as a configuration language, and I greatly prefer it to JSON, YAML, or Ruby, which are often used for similar purposes. The way arguments are passed in, merging of attribute sets, the library from Nixpkgs with various list and set operators and other utilities, and great multiline string support make it far superior to those other config languages in my opinion.
My biggest gripe is that the Emacs mode isn't very strong, so I'm often applying indentation manually.
I recently learned about Jsonnet[1] which has some similarity with Nix.
[0]: https://github.com/Gabriel439/Haskell-Dhall-Library [1]: https://github.com/Gabriel439/Haskell-Dhall-Library
Also, the comma-first syntax like
{ foo = "bar"
, baz = "qux"
}
drives me nuts. In the name of all good things in the world, why?
oooh but this is one of those trivial things that I really liked in my brief days of using Haskell, and that has sometimes carried over to my C++, e.g. in initializer lists. I find it super useful because everything lines up nicely, and from the point of view of version control you don't have to edit the previous line to change '}' to ',' when adding a new line.
foo := Foo{
x: 10,
y: 24,
}I wonder if people's aversion to commas first has something to do with the fact that commas have a very familiar use in ordinary writing, and there they always follow a word. It is very common to see BNF grammars written with leading vertical bars, and no one seems to mind that -- in fact it would be unusual to use trailing vertical bars.
By the way, a bunch of languages permit a trailing comma after the last element of a list. This is also nice because it means that if you put each list item on a line, the list items all have the same format (thing plus comma). With the Haskell syntax, you lose this, but there is no reason a language couldn't support a leading comma before the first element of a list.
Not if the language allows trailing commas on the last line, like Python and Go do.
Then you have a comma at the end in ALL lines, including the last one, without an issue. That was what I proposed above (and lots of languages do it, I think JS is adding it too).
- { old-one
+ { new-one
+ , old-one
While trailing commas allow you to insert an element anywhere with just one line addition.This doesn't break anything, but it makes looking at "git blame" so much easier.
None of the above language have all these properties combined.
But... I still love Nix, and you get used to it.
If yes, I can draft a proposal.
So, if only gradual changes are allowed, I would have started with:
- making semicolons optional
- commas for implicit array literals (e.g. "a", "b", "c" in lieu of ["a" "b" "c"]
- something to do with 'with' / 'in' syntax.
In fact, I can live with anything, except non-optional semicolons. Is there a possibility to infer them?
For the array litterals, I don't really see which improvements your syntax brings (except suppressing the space-separated list, which conflicts with the syntax for function application). Is it used somewhere else ?
https://gist.github.com/atemerev/889806081ed8fcb77495666fac9...
For me, it looks infinitely better.
I'm genuinely curious... what makes that a dealbreaker?
I'm someone who has worked in a large variety of languages, including Python (significant whitespace) and Javascript (no significant whitespace).
First: semicolon inference in Javascript is an abomination :). If the language says that whitespace is not significant, then using newlines to infer where semicolons maybe should be... is inconsistent with the statement "whitespace is insignificant".
Second: when I learned Python, I was coming from a primarily Perl and C background. The significant whitespace wasn't a big deal at all because that's how I indent my code anyway. And I would argue (and do in code reviews) that any code that is not indented properly is incorrect because it is extraordinarily misleading to a reader.
So, yeah, for any given programming language, I couldn't care less if whitespace is significant or not, or whether there's semicolons at the end of lines, but god damnit don't go half way on either of those. Indent your code as if whitespace is significant (because it IS to whoever's trying to decipher it later), and stick semicolons at the end of your lines whether or not some stupid inference rule will stick them there for you if you forget.
And as a final rant, "modern Javascript" didn't introduce semicolon inference; it's been there for a long time and was put there to try to lower the barrier for newbies. It wasn't put there because it's a great design decision.
An interesting thing with Scala is that in the BNF, "semi" is defined as ';' or nl.[2] After doing some initial reading about Scala, I was pretty much wondering why Scala needed semicolons at all. What you call "semicolon inference" seems like it's more like "optional semicolons", in that the language doesn't really need them.
[1]https://stackoverflow.com/questions/29743009/in-scala-are-se... [2]https://github.com/jystic/language-scala/blob/master/doc/syn...
If that was possible, and a few $000 was raised (looking at the comments here, I think people would chip in) then a new version of the Nixlang with the same semantics and full backwards compatibility but a cleaner syntax, would be a great project to fundraise for.
(Sexps are usually not my first choice, I think it is a "lazy option" for people who do not want to write parsers / design their own syntax. Even then, they are better than current Nix expressions).
There are some real advantages to having one universal syntax: witness the explosion in XML and, later, JSON (both of which are generally inferior to S-expressions[1]). It'd be pretty great for one person to write one parser, and then everyone forever after to be able to use it. We'd be able to focus on semantics and not on syntax.
[1] Although it is nice that JSON supports first-class associative arrays.
It's a volunteer-run service. It makes sense they don't want to host your proprietary binaries
(I don't see what's wrong with adopting sexps to avoid dealing with custom syntax -- quite the opposite,)
Anyway, thanks for the answer
EDIT: probably the only gripe I would make is to have comma-separated lists instead of having to use parentheses to separate non-atomic expressions.
Wait no separators are evil. This is the best thing about the syntax.
No need to cringe over syntax. Syntax is just syntax. If you are familiar with a better syntax to express Nix in then create a parser from that format to Nix' syntax. Then insert this parser as a pre-processor in the build process, and problem solved.
To me it doesn't feel weird at all: I can't think of any feature that was new or surprising when I started using it and I am not the biggest expert of functional programming. You have to learn more about the standard environment and tools if you want to start contributing to nixpkgs and write your own packages but if you only care about configuring your system nix is as difficult to learn as say, JSON.
It's true that some DSLs are not only different, they're also badly-designed languages or gratuitously different. I guess this is an occupational hazard of DSLs, but it's not unavoidable. It's possible to make the leap to a DSL only when actually beneficial and then to design a DSL that doesn't suck.
While so far the focus has mostly been on implementing the expression language as is, hnix being written in Haskell could be attractive for enriching the language with fancier features:
"Because now that it's in Haskell, now that a lot of other hackers could get involved, we could do things like add optional typing to the Nix language." — J. Wiegley
For more on hnix check out Haskell Cast's Episode 13 [1] with John Wiegley.
[0]: https://github.com/jwiegley/hnix
[1]: http://www.haskellcast.com/episode/013-john-wiegley-on-categ...
But I wonder if that will help or hinder it in the end. It's also unclear to me what Guile brings other than fracturing the Scheme/Lisp/Racket community further (there are other Schemes that are older, seemingly just as clean, and often more powerful). Seems like mostly NIMBYism, but I might be offbase
Do you mean NIH syndrome?
I think the one-type-fits-all ("attribute sets") vs. strong typing (disjoing record types) axis matters more than the static vs. dynamic axis here because most of the code (OS config files, package definitions, etc.) is loaded dynamically.
[0]: https://conda.io
(warning: it's very pro-Nix :) )
There are comparisons with alternatives in at least the propaganda for Spack and 0install, though the same criticism might apply to those -- I don't remember.
About the multiple versions, the problem with "classical" package managers is that you have to do some manual renaming to ensure that both versions won't be installed in the same place in the file-system, which is tedious and doesn't scale well. Furthermore you may encounter some problems because both will be visible at the same time if the packager isn't careful enough. For example, if a software X has a dependency of libfoo2.1 and you happen to have libfoo3.1 also installed, the installation script for X may use libfoo3.1 instead of libfoo2.1, in which case you risk to encounter bugs because X hasn't been tested against libfoo3.1.
>You can also do unprivileged installation into a separate root with, say, fakechroot or PRoot
Yes, but that's the only way to (safely) use such package managers without privileges; so that's not viable to do for production.
>and you need something like that to install and run Nix unprivileged, don't you?
Install, yes. Run, no. Setting up Nix does require root, but once it's installed on a system it can be safely used without privileges by all the users on the system. That's very valuable in itself, but it's especially interesting when you consider providing access to a single Nix store over a network filesystem, or providing it to multiple different virtual machines or containers.
My Guix installation runs using a with privileged access to the store, and Nix is the same as far as I know. I don't know why Nix would be a particular advantage on our HPC networked filesystems, and it's not clear it's tenable for users to be able to DoS the store by installing an arbitrary amount of software in limited shared space.
It may well be that Nix makes the right set of trades-off against other possibilities for a given situation, but thta seems at least not completely clear cut.
Conversely in Guix most of the data structures are disjoint record types. Scheme (the implementation language of Guix) is dynamically-typed, but there are sanity checks we can do on records both at macro-expansion time and at run time, such as checking whether all the required fields are defined and no extra field is passed. Concretely, this means that users get clear syntax errors or run-time type errors.
Nix and Scheme are both dynamically typed, but they have a different typing story.
Disclaimer: Guix hacker here.
- baked in fetchurl, fetchgit for easier bootstrapping
- a functional dsl for rendering filesystem hierarchies - instead of the find/sed/awk galore. 50% of the time when my nix recipe breaks it's not a nix syntax issue but something with bash. (reading nix is an exercise in learning new unix features)
- drop the channel feature entirely and make releases immutable tar files with hashes. I use nix as a build system on macos and right now a nix-channel update is a sure way to break all my builds which is the opposite of nix's promise
EDIT: formatting
So the CTA "Get Nix" is kinda funny for German speaking people :)
PS: Greetings from Austria!
It's not exactly in the same space as Nix, but not very far from it, either. In Habitat, you use a language that felt to me quite similar to Arch Linux's PKGBUILDs, but extended with container-specific things like service ports etc. Habitat outputs containers that can be started in various orchestration technologies, like Kubernetes or Mesos/Marathon.
Off topic but this brings up a good point I've been curious about for a long time. Does anyone know why VimL is such a terrible language?
It's fascinating to think what potential it could have with a modern language natively supported like this. The use of Python/Lua etc seem like complicated hacks on top of it, not to mention the API with tons of globals.
Regardless Wikipedia says:
> Vim script (also called vimscript or VimL) is the scripting language built into Vim.
https://www.wikiwand.com/en/Vim_(text_editor)#/Vim_script
It's also colloquially referred to VimL across the web.
For those looking for an answer, the 3rd result from googling "VimL" is a question on StackOverflow "Why does VimL suck?" which does a good job of answering my question:
https://www.reddit.com/r/vim/comments/1bf672/why_does_viml_s...
Half-OT: How is the new CLI coming along? AFAIK there was an effort to make Nix usage a bit more like known from other package managers.
The design is at https://gist.github.com/edolstra/efcadfd240c2d8906348
The issue is at https://github.com/NixOS/nix/issues/779
Is this an ongoing process and most of it is already released or is this still in the making?
E: In the paragraph titled "NIX TODAY".