Runhaskell no longer works with globally installed packages

I have a couple of scripts (not a cabal project, invoked via runhaskell) that relied on the default packages that ship when ghc is installed (via ghcup) (ie, output of ghc-pkg list => base, containers, etc).

It’s been a couple of months since I’ve touched those scripts, and since went through a couple of ghc updates, and today when I try to invoke them it complains that I’m importing hidden packages.

Is this a change that took place recently?

Or B), I’ve mucked up my environment somehow? I did play around with xmonad recently and installed both the binary and the libs globally via cabal, but not sure if that could somehow make changes for this specific issue.

This could be what causes it. If you don’t have a .ghc.environment file then it may use the libraries included with GHC, but if you install libraries globally with cabal then it will create a default environment file which may hide the included libraries.

To avoid having to fiddle with environment files, I would recommend using cabal scripts: 1. Getting Started — Cabal 3.12.1.0 User's Guide. It has a lightweight syntax to specify dependencies and you can run them easily using cabal run MyScript.hs.

2 Likes

Thank you. I’ve switched to a newer version of ghc for which the packages haven’t been altered globally. I’ll live without an LSP for a couple of days/weeks.

With a “reinstall” attempt I managed to brick my 9.8.2 environment. rm -rf “~/.cabal/store/ghc-9.8.2” + ghcup remove + install ghc-9.8.2 /= fresh install of ghc-9.8.2

I’m aware of cabal run, I prefer runhaskell in this scenario.

You should be able to safely clear the global environment by doing something like this:

rm ~/.ghc/x86_64-linux-9.8.2/environments/default

(but that can break your xmonad setup)

1 Like

Can I ask why? cabal run really ought to just do what you want.

1 Like

Thanks again, that fixed my package environment. Might open an issue in GHCup to have it remove those files when a GHC version is uninstalled.

I’m not familiar enough with the newer cabal features to replicate what I’m doing, my workflow, and didn’t want to get distracted to learn a new tool (cabal run, in this case).

These scripts I’m writing, contain their own modules that I can make accessible by passing along the ghc includepath parameter.

#!/usr/bin/env -S runhaskell --ghc-arg=-i/home/mhitza/scripts/

When I need to, I can compile the script and get an executable in the same directory. Whereas I don’t know where a cabal build file.hs places the executable.

I want to avoid managing a cabal file for this project.


I think cabal run would annoy me if I where to use it for my scripts during the write/debug cycle, as the compilation behind the scenes does not make for good DX. This is on an 8th gen i5 laptop Intel CPU. (when I switch to powersave mode those values double, which is still pretty snappy with runhaskell)

/tmp/example.hs:

#!/usr/bin/env cabal
{- cabal:
build-depends:
  base ^>=4.19.0.0,
-}
main :: IO ()
main = putStrLn "Hello, Haskell!"
/tmp » time runhaskell example.hs
Hello, Haskell!
runhaskell example.hs  0.18s user 0.08s system 98% cpu 0.260 total

/tmp » time ./example.hs         
Hello, Haskell!
./example.hs  0.97s user 0.35s system 99% cpu 1.327 total
2 Likes

OK, it’s up to you, but if you try to avoid cabal (or stack) then I think in the long run you’re going to have a very bad time.

3 Likes

Reasons to avoid cabal run:

  1. cabal run with all of its up-to-date checks has a noticeable boot time (as noted in a previous post). Humans perceive reaction times of 250ms and below as instantaneous, everything above is perceived as delay that might destroy the flow experience.
  2. cabal run might print extra stuff to stdout which isn’t good if pipe the output into another program.
1 Like

To solve 2 I think I’d just suggest cabal install --installdir=. --overwrite-policy=always, since that seems the behaviour that OP is used to from ghc.

Regarding 1, I’m actually surprised there’s no runghc equivalent in cabal (or is there?).

It’s been talked about. In fact it looks like the blocker there has now been implemented.

Although I’m not sure that actual compilation is the main reason Cabal is slow here.

1 Like

Cabal’s boot time should be significantly improved now that perf: Group together packages by repo when verifying tarballs by mpickering · Pull Request #10112 · haskell/cabal · GitHub + Improve deserialisation performance by alt-romes · Pull Request #95 · haskell/tar · GitHub have been merged.

These improvements will be available in 3.14, and have also been backported to 3.12 (though I think a release containing these backports has not come out yet)

4 Likes

If not compilation that something very similar to it. On subsequent runs cabal run is significantly faster, ~40ms on my machine (~4x faster than runhaskell), as long as I don’t change the file.

Actually I was mistaken. I was going to say that what’s annoying is when you’ve made changes to the body of the code but not to dependencies or other flags. But actually Cabal is cleverer than I thought in this scenario, and avoids doing any solving or configuring. I assume this was part of the recent-ish improvements to script support.

This is caused by Cabal commit 5cb8475. Prior to that change (i.e. 3.8 and older), when cabal created a new environment file, it put most if not all of the boot libraries in it automatically. But now (i.e., 3.10 and newer), it doesn’t do that anymore. See also Cabal issue #8894 and my StackOverflow question about a workaround for this.

2 Likes