[ANN] Purifix - The PureScript Package Manager in Nix

I’d like to announce purifix, a small package manager for PureScript written in
Nix. Purifix works with the PureScript Registry and can fetch your dependencies
either from the registry or directly from git repositories or your file system.
Purifix doesn’t require you to generate a nix expression that you save to disk,
instead it parses your spago.yaml file and figures out how to compile your
package from there.

By leveraging nix, purifix can deduplicate your package dependencies and make
sure that a specific version of package can be reused between your PureScript
projects without having to recompile. This means that getting started on a
new project can be much faster as you only need to compile the files that are
unique to this new project.

Purifix is rich in features and supports anything from bundling to running tests
and generating documentation.

A project-tailored development shell that exposes a purifix executable is
also provided. This executable allows you to incorporate the pre-compiled
dependencies of your package so that you’re only building your own files when
you’re developing your packages. The development shell is also compatible with
the purescript-language-server provided you have told the language server to
find projects with a spago.yaml file.

Purifix is constantly tested against the PureScript Registry and can build all
packages published there. Purifix makes use of easy-purescript-nix to ensure
that you’re using the right compiler for the package set you’re depending on so
that you know that all packages will compile correctly.

15 Likes

This is a really cool project and I can’t wait to switch to this from spago2nix once I’m using the new spago. Thanks for all your hard work!

3 Likes

Just tried it out, it works really great!

I dream for a day when this is possible without IFD

Thank you for showing interest in this project!

Thank you for testing it out! Glad to hear it worked for you :slight_smile:

Depending on your definition of IFD it might be possible to achieve most of purifix without IFD. It is very convenient to rely on IFD, however.

Purifix currently uses IFD to:

  • Convert the spago.yaml file into a JSON encoding that nix can read
  • Extract the metadata (dependencies etc.) from the purescript-registry-index from the JSON lines encoded files
  • Figure out what the relative path to a package is in a repo (using realpath), this is an issue with subpaths in nix being too strict and not supporting parent directories ../

Also, this use of IFD doesn’t actually import the files but purifix works using builtins.readFile and builtins.fromJSON to obtain its required data. I don’t know how you’d convert YAML to JSON inside nix without depending on IFD (maybe purenix could help?) but extracting the relevant data from the metadata on the registry-index should probably be doable in nix alone.

Here are the uses of readFile:

default.nix:  flake-lock = builtins.fromJSON (builtins.readFile ./flake.lock);
nix/build-support/purifix/default.nix:  spagoYamlJSON = fromYAML (builtins.readFile spagoYaml);
nix/build-support/purifix/from-yaml.nix:builtins.fromJSON (builtins.readFile (stdenv.mkDerivation {
nix/build-support/purifix/get-package-set.nix:  registryPackageSet = builtins.fromJSON (builtins.readFile package-set-file);
nix/build-support/purifix/get-package-set.nix:      meta = builtins.fromJSON (builtins.readFile "${purescript-registry}/metadata/${package}.json");
nix/build-support/purifix/get-package-set.nix:            relative-path = builtins.readFile (stdenv.mkDerivation {
nix/build-support/purifix/get-package-set.nix:      meta = builtins.fromJSON (builtins.readFile (stdenv.mkDerivation {
nix/build-support/purifix/get-package-set.nix:      targetPursJSON = builtins.fromJSON (builtins.readFile "${meta.src}/${meta.subdir or ""}/purs.json");
nix/build-support/purifix/get-package-set.nix:      targetSpagoYAML = fromYAML (builtins.readFile "${meta.src}/${meta.subdir or ""}/spago.yaml");
3 Likes

IFD is really not nice, haskell.nix did this way, and it becomes really slow on evaluation time. I also can’t see how purenix would be used to solve this, I mean you could use it to write a program that parses the yaml to nix expression, but that’s only reinventing the wheel.

NixOS/nix#5373 would be a awesome feature to be able to extract yaml info as flake inputs and lock things through a computation, discarding the need of IFD.

3 Likes

I found this debate between maintainers of haskell.nix and horizon-platform. They’ve also mentioned purenix as a solution for IFD.

2 Likes

@klarkc Thanks for linking to the above discussion. With callCabal2NixWithoutIFD @cdepillabout showed that it’s possible to get rid of IFD for these kind of tools if you write the configuration parsers in nix directly.

That’s where purenix comes in, you can write a parser more easily (in my opinion) in purescript and then have purenix compile it to nix so that you can avoid doing the parsing in a nix derivation.

5 Likes

There is any chance of turning spago yaml optional? This match my use case: I want to reduce the number of config files to bare minimum and use package set as a flake input (so I can use the flake as single source of truth).

This is what I am doing here with purs-nix. purs-nix allows me to override the package-set with my own package-set from the flake input (ctl-nix), having the cardano-transaction-lib dependency.

3 Likes

@klarkc There’s an open issue for that: Allow specifying registry version and extra packages directly in nix · Issue #13 · purifix/purifix · GitHub

Apart from the yaml files there’s still a few instances of IFD that would be nice to iron out. There are a couple of things I’d like to focus on before tackling nix-only config. I do want to be able to bypass the parsing of the spago.yaml files in the future, though.

4 Likes

That’s awesome! I’ve played around with purs-nix a bit, and one thing that botters me now is having to bump the package-set repository manually (I mean beyond a simple nix flake update) whenever the upstream changes. Having something built into purifix to do this job would also be great (but also, maybe this is a job for another tool).

2 Likes

@klarkc just fyi purs-nix will definitely be able to process the new purescript package sets in the future. It just hasn’t been a priority for me because afaik they are not official yet. But given that purifix is doing it right now, perhaps there was an announcement I missed.

4 Likes

@ursi That’s really nice.

I was wondering why both projects are “competing” for the same goals, given that they have a shared landscape. Perhaps a better approach would be to have Purefix focus on being the package/library user face, while Purs-nix takes on the role of the package-set maintainer. By dividing the responsibilities this way, the two projects can complement each other and improve on different aspects of the integrated landscape. What do you guys think?

4 Likes

The way I currently see it is that purifix is focusing on taking the spago workflow and seamlessly integrating it into nix, whereas purs-nix is a standalone tool/ecosystem. I think there are probably enough differences in these approaches to warrant separate tools, and presumably the creators of purifix thought the same way.

3 Likes

@Adrielus @klarkc

I’m happy to announce that purifix now can be used without IFD.

There’s still one remaining use of IFD in the purifix repository that stems from parsing the spago.yaml files. However, now purifix supports loading purifix.json files that are JSON encoded files with the same format as spago.yaml.

Here’s an example of how purifix can be used with the purifix.json files:

5 Likes