Skip to content

Conversation

@sellout
Copy link
Contributor

@sellout sellout commented Dec 11, 2025

This replaces haskell.nix with stacklock2nix, and brings everything up to Nixpkgs 25.11 and Stackage LTS 24.21 (GHC 9.10.3).

It also adds documentation for how to upgrade tooling in future.

This is an alternative to #6033 + #5914.

Implementation approach and notes

The first commit switches the infrastructure from haskell.nix to stacklock2nix. The latter is much simpler, but less flexible. We took advantage of none of the flexibility of haskell.nix (nor do I think there’s any reason to), but we had a lot of overhead and complexity due to it.

However, it’s difficult to switch the infrastructure without also updating the other tooling (Stack, GHC, etc.), so the remainder of the PR is largely about supporting the new tooling, mostly the upgrade from GHC 9.6.5 to 9.10.3.

Loose ends

There are two warnings downgraded from errors: deprecations and x-partial. I have changes to make them errors again, but I think they’re better as separate PRs (x-partial in particular has a nice solution, but requires threading a lot of changes).

unison-runtime:optchecks is failing, and I’m not sure why. Would love some assistance with this one.

@sellout sellout force-pushed the replace-nix-tooling branch 7 times, most recently from bbd54ca to 77fca19 Compare December 15, 2025 06:22
@sellout
Copy link
Contributor Author

sellout commented Dec 15, 2025

@ChrisPenner This PR upgrades us to GHC 9.10 … and I think the compiler must be doing something differently somewhere, because the optchecks stuff is failing with effectively no change to unison-runtime. Maybe it just needs more of that throwIO workaround or something, but thought you might have a clue to offer.

@sellout sellout force-pushed the replace-nix-tooling branch from 77fca19 to 0a324a9 Compare December 23, 2025 22:34
@sellout sellout force-pushed the replace-nix-tooling branch 2 times, most recently from e5e515c to 2b3784b Compare January 6, 2026 22:41
@sellout sellout marked this pull request as ready for review January 6, 2026 22:51
@sellout sellout requested a review from a team as a code owner January 6, 2026 22:51
@aryairani
Copy link
Contributor

unison-runtime:optchecks is failing, and I’m not sure why. Would love some assistance with this one.

@dolio If you might have an idea on this; possibly due to ghc version change?

@dolio
Copy link
Contributor

dolio commented Jan 7, 2026

That test checks that GHC is generating code that looks properly optimized. I think it basically checks that the eval function contains no mention of the Stack type.

So, the details of the optimization have changed. I guess you could try running benchmarks before and after to see if the difference actually matters. If it doesn't, then maybe we need a different test. But if it does, we need to figure out how to fix the performance. Even if it doesn't, it might be easier to tweak something to satisfy the check than to figure out a new check that's actually proper.

sellout added 12 commits January 7, 2026 16:04
This is much simpler Haskell tooling that serves our needs and keeps us
on a better cache (NixOS).
- resolve various implicit import conflicts (`show`, `unsnoc`, `unzip`, etc.)
- update `text-builder` names (`Text.Builder` -> `TextBuilder`, `run` ->
  `toText`)
- update primitive usage (`keepAlive` and
  `withMutableByteArrayContents`)
The new `-Wx-partial` warning and some deprecations require more
involved changes, so defer for now.
There was a comment about this before, but the failure caused by having
both GHC 9.10.3 and 9.6.5 HIE files in the GitHub actions cache made it
worth addressing now. This also works around another bug introduced in
Weeder 1.9.0.
Deep file creation was causing tests to modify files in `$HOME`, which was exposed by Nix sandboxing. This moves the
file creation out a bit so that test code can specify an alternative location (alongside the test codebase).

I’m not sure this is the right level – should we just not manage credentials during a test? Have a null manager? And
this really should be moved up even further.
This is a workaround for an issue with Nix on macOS. TLS on macOS
requires access to the `security` binary, which is not available to Nix.
This creates a non-TLS HTTP client for the transcript tests to avoid
that issue.
@sellout sellout force-pushed the replace-nix-tooling branch from 2b3784b to b78c7f0 Compare January 7, 2026 23:04
@sellout
Copy link
Contributor Author

sellout commented Jan 7, 2026

Here’s the Nix workflow run on my fork: https://github.com/sellout/unison/actions/runs/20785608716 (it’ll run on trunk in this repo when this PR is merged).

Copy link
Contributor

@ceedubs ceedubs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the docs for building with cabal need to either switch back to project-file or need to otherwise give a working command (perhaps moving cabal files out of contrib or suggest adding a symlink). Otherwise this is working well for me.

__NB__: The hpack binary in Nixpkgs is for 0.38.3, but the library
        Stack is built against is 0.38.1 for some reason.
Also enable it on aarch64-linux for the first time.

Fixes unisonweb#5789.
@sellout sellout force-pushed the replace-nix-tooling branch from b78c7f0 to bca81de Compare January 9, 2026 19:19
@github-actions
Copy link

github-actions bot commented Jan 9, 2026

Some tests with 'continue-on-error: true' have failed:

  • Cabal / smoke-test

Created by continue-on-error-comment

@sellout
Copy link
Contributor Author

sellout commented Jan 9, 2026

I think that the docs for building with cabal need to either switch back to project-file or need to otherwise give a working command (perhaps moving cabal files out of contrib or suggest adding a symlink). Otherwise this is working well for me.

I removed the commit with the Cabal doc changes. I prefer the shorter --project-dir=contrib over --project-file=contrib/cabal.project, and I like the paths to be relative to the file they’re in … but it breaks the ln -s contrib/* ./ workaround, which is nice for people who are using Cabal regularly.

@sellout
Copy link
Contributor Author

sellout commented Jan 9, 2026

NB: An annoying thing here is that because of the way the Cabal workflow works (in order to allow forks to make comments on PRs), it’s going to fail until after this is merged – it uses the workflow file from the target branch (trunk), and that means it gets the old GHC (& Cabal) version.

Copy link
Contributor

@ceedubs ceedubs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't claim that I've reviewed the Haskell changes, but the Nix changes seem good to me 👍

@aryairani
Copy link
Contributor

Ok but the current build failures aren't nix related right?
They're something weird about the optimization checks that needs to get fixed for ghc 9.10.3; so not currently mergeable.

@sellout
Copy link
Contributor Author

sellout commented Jan 12, 2026

@dolio I tried running the benchmarks, but they don’t seem to build

⚙️   Processing stanza 1 of 15.
  ❓

    An error occurred while running the following file: unison-src/transcripts-manual/benchmarks.md

    ``` ucm :hide
    > pull unison.public.base.releases.M4d base
    > pull runarorama.public.sort.data sort
    ```

    🛑

    The transcript failed due to an error in the stanza above. The error is:

    ```
    I think you want to merge unison.public.base.releases.M4d into
    the base branch, but it doesn't exist. If you want, you can
    create it with `branch.empty base`, and then `pull` again.
    ```

At first I thought I was just doing something wrong, but I’ve had the same thing happen on various releases. Granted, it’s still possibly I’m doing something wrong. I‘m now seeing if it works for me at the point the benchmarks were last updated. If not, then it’s very likely I’m doing something wrong.

Either way, the benchmarks should probably be run as part of CI. We don’t need to actually measure them in CI (GitHub is no good for that), but at least ensure they build, so then I’d immediately know what I’m doing wrong.

@aryairani
Copy link
Contributor

aryairani commented Jan 12, 2026

> pull unison.public.base.releases.M4d base

This is ancient syntax, so those aren't the right benchmarks. Those should be updated or deleted.

@dolio what are the right benchmarks that @sellout should run? Is unison-src/transcripts-manual/benchmarks.md a thing? Or, can you coordinate with them, thanks.

That test checks that GHC is generating code that looks properly optimized. I think it basically checks that the eval function contains no mention of the Stack type.

So, the details of the optimization have changed. I guess you could try running benchmarks before and after to see if the difference actually matters. If it doesn't, then maybe we need a different test. But if it does, we need to figure out how to fix the performance. Even if it doesn't, it might be easier to tweak something to satisfy the check than to figure out a new check that's actually proper.

@dolio
Copy link
Contributor

dolio commented Jan 12, 2026

The benchmarks are on share

https://share.unison-lang.org/@pchiusano/misc-benchmarks

run suite after pulling.

I don't actually remember what is in the benchmark transcript.

@sellout
Copy link
Contributor Author

sellout commented Jan 13, 2026

Here are the benchmarks (times in µs). With one nasty exception, things look good. I re-ran them to see if there was some interference with that outlier, but everything looked the same.

  GHC 9.6.5 GHC 9.10.3 change benchmark
✔️ 110.726 108.530 -1.98% Mutate a Ref 1000 times
✔️ 45 410.000 43 397.000 -4.43% Mutate a local Remote.Ref 10k times
✔️ 90.020 85.898 -4.58% Do 10k arithmetic operations
✔️ 141.426 138.903 -1.78% List.map increment (range 0 1000)
✔️ 578.737 391.878 -32.29% List.map murmurHash (range 0 1000)
✔️ 70.645 62.998 -10.82% Multimap.fromList (range 0 1000)
✔️ 5 767.393 5 603.962 -2.83% Stream functions
3 265.559 10 221.946 213.02% Value.serializeUncompressed (10k element map)
✔️ 23 243.425 18 514.487 -20.35% Value.serializeCompressed (10k element map)
✔️ 27 514.550 21 939.512 -20.26% Value.deserializeCompressed (10k element map)
✔️ 8.914 7.961 -10.69% Json.toText (per document)
✔️ 9.708 7.127 -26.59% Json parsing (per document)
✔️ 14.671 10.520 -28.29% Json complex parsing (per document)
61.385 62.808 2.32% Json complex decoding (per document)
✔️ 0.174 0.170 -2.30% Decode Nat
✔️ 84.720 82.710 -2.37% Generate 100 random numbers
1 169.958 1 171.398 0.12% List.foldLeft
✔️ 43 123.512 42 159.812 -2.23% Count to 1 million
  0.079 0.079 0.00% Count to N (per element)
✔️ 79.544 79.209 -0.42% Count to 1000
✔️ 125.211 122.582 -2.10% CAS an IO.ref 1000 times
✔️ 0.045 0.042 -6.67% List.range (per element)
✔️ 61.784 55.654 -9.92% List.range 0 1000
✔️ 29.812 29.658 -0.52% Set.fromList (range 0 1000)
✔️ 28.705 28.232 -1.65% Map.fromList (range 0 1000)
✔️ 2 660.596 2 603.665 -2.14% NatMap.fromList (range 0 1000)
✔️ 0.190 0.178 -6.32% Map.lookup (1k element map)
✔️ 0.265 0.254 -4.15% Map.insert (1k element map)
✔️ 1 352.450 1 311.557 -3.02% Shuffle a 1000 element array
✔️ 3 135.777 3 097.065 -1.23% Mutably mergesort a 1000 element array
✔️ 0.145 0.143 -1.38% List.at (1k element list)
2.742 2.803 2.22% Text.split /
✔️ 5 720.393 5 496.593 -3.91% Two match
✔️ 5 905.034 5 668.678 -4.00% Four match
✔️ 5 344.256 5 183.918 -3.00% Thirty match
✔️ 137.770 133.497 -3.10% fib1
✔️ 327.592 304.789 -6.96% fib2
✔️ 331.159 303.199 -8.44% fib3

@ceedubs
Copy link
Contributor

ceedubs commented Jan 13, 2026

❌ 3 265.559 10 221.946 213.02% Value.serializeUncompressed (10k element map)

For what it's worth, this benchmark looks to be using Value.serialize_v4. Most things would be using v5 (Value.serialize) these days, so that would probably be the more relevant benchmark. It probably isn't a big deal if v4 serialization gets twice as slow. Though it's also strange because the compressed version of the test still using serialize_v4 and then just sends it through zlib compression/decompression. That makes it seem like either a fluke or that there was a boost in the zlib performance that is offsetting a performance regression in the v4 serialization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants