State of the Cabal - Q1+Q2/2021

This is what stack does. Just replace ~/.cabal/top-level.project with ~/.stack/global-project/stack.yaml.

2 Likes

Wow, thank you @kindaro, eloquent and well-written :clap:

Thank you o serpent of precious feathers. Your kind words really help.

Here’s a random data point for what it’s worth: I gave haskell a shot before Stack, and I gave up solely due to cabal and the ux it provided. … I have tried cabal a few times over the years since I picked up haskell in more earnest, mainly after the v2 commands came up, and again a few months ago. I couldn’t really tell that anything seriously improved/changed. I mean, I know there is new functionality, but the philosophy and hap-hazard organization is still the foundation. To this newb and the newbs I’ve worked with, the tool makes no sense, is frustrating to use, not discoverable, and provides little in the way of a holding hand thru the madness that is building software.

I sympathize with this. And I also went with Stack for a while. But the most straightforward way to make things better is to make things better, that is to say, improve Cabal rather than maintain a baroque wrapper. And I believe that with Emily on our side we can do that.

But we need actionable intelligence first. That would be a collection of tickets describing the issues you and your friends are having with Cabal, user experience and all. There is no one that can file these tickets but you. Once again, Emily being here I believe it is safe now, but you can also open issues in Tilapia — it is protected by the guardian spirit of a jaguar. Without such evidence as you may provide, improvement is impossible.

A next step would be to attach a tag «user experience» to those tickets and find someone to try and imagine what that improvement might look like.

1 Like

cabal-env also does something like that but it stores the constraints in the default package environment of GHC. It has been working nicely for me.

I like the global environment too, mostly to install a set of basic packages that I want to use many times like bytestring, text, vector, criterion, optics, then I never have to create a whole cabal project if I only want to use those packages.

One of my use cases is copying random code from the internet, pasting it into a .hs file and trying to compile it or load it into the repl. Maybe even writing a small benchmark around it.

3 Likes

We could link all the cabal issues with cabal v2-install --lib, users trying it usually want a non-project centric workflow, but we could do a search in the cabal issue tracker too.
In the fp discord server i’ve seen several users struggling with setting dependencies for ghc for exploratory or pedagogical purposes (like mentioned ones by @rae). Non project-centric workflows are usual in contexts where you try to help beginners with ghc, without introducing them in all the complexity of a stack/cabal project (cc @edmundnoble) .
Iirc xmonad scripting also needs to have dependencies in scope without a project (cc @liskin) .

stack somewhat fixes the issue having a global project configured in its global config dir. Not sure if cabal could do something alike, but it tries to be more explicit in general and maybe an implicit global project will not match its (actual?) design principles. As mentioned above @phadej cabal-env has been used and afaik succesfully as a replacement fot cabal v2-install --lib.

The ticket for the cabal install --lib replacement aka cabal env is this by the way: https://github.com/haskell/cabal/issues/6481

2 Likes

I strongly agree with this sentiment. I invoke GHC dozens of times each day, in lots of different directories. I really want to be able just call it, passing the flags that I decide.

I think of installing packages globally as like putting things in my global .ghci file – it just sets my global defaults. E.g. I don’t think anyone is advocating making ghci not load the Prelude (a current global default) unless you invoke it from cabal. That is, making GHC usable only via cabal.

This seems like a rather simple workflow to support. Is it problematic, really?

3 Likes

Switching from «use a sandbox, rebuild the whole world every time, get passive aggressive looks from Greta» to nix-style builds was paradisiac to me. Same with install --lib, saner defaults for init, etc.
I am happy to have witnessed cabal growing up.

2 Likes

I’m another user of cabal install --lib and package environments. For stuff like writing some quick example code to post somewhere, I don’t like to create full-blown packages, and prefer to work with ghci directly. It also feels snappier.

I tend to create local environments with cabal install --lib --package-env .. Partly because, on Windows, there was a bug (now solved) that put the global environments in a place GHC didn’t look for.

Admittedly, cabal install --lib is an ungainly command, becase the invocation without --lib does something which is very different.

I believe cabal env is the correct way forward, and eagerly await its inclusion into cabal-install. I don’t think the -v1 commands should keep being supported once cabal env arrives.

That said, some effort should be devoted to remind library authors to change pervasive obsolete documentation like

Install it in the usual way:

$ cabal install

which doesn’t work even now and is a source of grief for newcomers. Perhaps Hackage could display automatically generated and up-to-date installation instructions along package READMEs.

3 Likes

So, the summary so far is that many people like to use ghci with a global environment, and they still use Cabal to put packages into said environment. Therefore Cabal should support this use case — but it does not mean it should support it via v1 commands, right?

I am going to explain why I prefer to create temporary packages (cabal repl --build-depends … and cabal scripts), even though they are slow to launch.

  1. I want some language extensions and modules to be available in ghci immediately, but it opens a space for conflicts, as I mentioned above.
  2. I have no idea how to manage broken environments, and broken global environment is going to necessitate my removing of all caches and a few hours lost.
  3. I actually have no idea what an «environment» is in the first place and I am yet to see a beginner friendly «how to» guide.
  4. I once asked how to uninstall a library and was told there is no way.

The bottom line is that there are two ways to manage sets of packages and settings: environments and packages. This means twice the bugs and twice the learning curve. Solution? Officially support this use case in Cabal.

Is cabal env the thing? I am not sure. Can I configure an environment the same as I configure a package? Or do I need to learn a different configuration language? That would be unfortunate.

2 Likes

My three workflows:

  1. Build a collaborative project in exactly the same way that my collaborators do, every time, so that “works for me” is a distant memory, and so we can reuse build results.
  2. Build throwaway prototypes quickly, so I don’t have to wrangle a metadata file just to show off a quick idea
  3. Yolo some new packages and try to learn about them without “committing” to it.

How I satisfy these workflows:

  1. Cabal + Nix + Nixpkgs pins. I get precisely the same deps every time, and no rebuilding!
  2. NixOS + ghcWithPackages to build a Haskell Platform-lite in my system description.
  3. nix-shell -p 'haskellPackages.ghcWithPackages (p: [ p.fancy-package ])'

This will work for me for the forseeable future. All I can imagine doing is swapping out some of the implementation details. For instance, in my ideal world, cabal would be a unixy tool that calculates build deps which I would feed into the Nix furnace. That would make it pretty easy to migrate a project from the second or third workflow into the first workflow, should the opportunity arise.

2 Likes

@kindaro some clarifications:

This was partially true for package databases, but environments are just a set of “pointers” to actual packages in the cabal store, so deleting and recreating them is very quick, and you don’t have to delete your cache.

There is some documentation about them at the ghc side: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/packages.html#package-environments

The install --lib docs explain the practical usage with current cabal: https://cabal.readthedocs.io/en/3.4/cabal-commands.html#cabal-v2-install

cabal env will of course have its own section

With cabal env, there will be a way to remove a package from an environment (the prototype linked in the issue can already do this)

Or, if you mean actually freeing up the space from the cache, there is #3333, but it’s lower priority

2 Likes

There is also a prototype garbage collector called cabal-store-gc in the cabal-extras-suite.

3 Likes

Right, but then the build is not very well-guaranteed to be reproducible.

This sort of “design philosophy” is unfriendly to users and the sort of thing that leads us to give up on the tool. If I can not sufficiently figure out how to use the tool, in a reasonable amount of time, the tool is a distraction and probably not worth the time invested.

Stack lets me be productive while I also continue to learn about haskell, the tools, how the packaging ecosystem works, etc. Cabal prevents me from learning until I have learned enough to use it in basic ways. That’s a really frustrating, but also avoidable.

4 Likes

Yes, but I only use it for one-shot purposes; to try out things quickly and then forget about them. So reproducibility is not a concern.

1 Like

xmonad scripting also needs to have dependencies in scope without a project

Indeed, xmonad invokes ghc to recompile itself and it expects the xmonad and (usually) xmonad-contrib libraries in scope. That has probably been broken since cabal switched to v2 by default. I’m not a cabal user myself, but I am told that cabal install --lib --package-env=$HOME/.xmonad/ works these days just as well as v1 install did. I shall try this somewhere and update our documentation before the next release.

Many of our users use stack and the same problem is there as well, without as nice a solution. We now tell users to create a custom build script that invokes stack exec ghc … from wherever their stack.yaml is. That’s silly and we should probably at least check for ~/.xmonad/stack.yaml and use that to make common setups easier.

To add to the rest of the debate, I’ll just note how I personally deal with what you folks use cabal install --lib for: I’ve been a user of stack for a while, and I usually commit to a single version of Stackage LTS that I use in both the global ~/.stack/global/stack.yaml as well as the stack.yaml that I use for xmonad and arbtt and ghcid and other tools. Therefore, whenever I stack ghci in my home or /tmp or wherever really, all the packages that have already been built from that LTS are available, and if not, stack build something is always happy to compile an additional package from the LTS snapshot and make it available globally.

Committing to a single old LTS version is obviously a rather conservative approach, but it usually works for me. Yeah, I happen to favor the “less deps, and if necessary, prefer boring deps” approach. :slight_smile:

1 Like

I’m not trying to put judgement on the practice, just focusing on the technical limitation. For example, if you were offering support to other haskellers, or otherwise posting the results to the web (where haskellers can read and learn from it), reproducibility is a valuable feature, and without it, the code decay is rather surprising.

2 Likes

While it’s been common in *nix for a while, to have an app like xmonad use a system-provided tool like ghc… there is an opinion on isolation and coupling that is growing on me: cat and mkdir, rm, etc are self-contained and have stable APIs, whereas the software used to build and run some other software is generally much more coupled, and in some cases highly sensitive to variances or details in versioning, configuration, etc, such that it actually makes a lot of sense to shove the coupled components together in isolation. In fact, this works so well we have built nix and nixos on the general idea. So while I understand how an app like xmonad has done what is has, it also seems to make sense to come up with solutions that give the app (xmonad/etc) a better and more efficient experience working with the coupled software, and in isolation from the rest of the system. This is partly why I shy away from tools and workflows that don’t provide the structures to make that type of efficient interaction and isolation the norm.

2 Likes

FYI xmonad works perfectly well with a custom cabal v2- build script, viz.

2 Likes

I used to do that, but cabal is quite slow to start even if nothing has to be recompiled, so now I use a makefile that uses cabal if files have changed. Oh, and you might want to add --overwrite-policy=always.

1 Like