Skip to content

Instantly share code, notes, and snippets.

@hasufell
Created June 10, 2024 13:33
Show Gist options
  • Save hasufell/5ccb0cea382ec9a50c9fd06c72f86b88 to your computer and use it in GitHub Desktop.
Save hasufell/5ccb0cea382ec9a50c9fd06c72f86b88 to your computer and use it in GitHub Desktop.
Revive cabal sandboxes

Motivation

At ZuriHac 2024 we (@andreabedini, myself and others) talked with @ivanperez-keera and @fdedden about the user experience of using GHC and cabal for people who are not Haskell developers and are not interested in Haskell on its own, but rather use it to:

  • build a package
  • use a framework/library that requires Haskell (e.g. copilot)

These users are not interested in:

  • nix
  • Haskell
  • cabal
  • content addressable storage
  • declarative environment configuration

The cabal v2 commands are assuming a lot of these things UX-wise.

We are well aware that there are ways to utilize store location and environment files to achieve something similar. But this misses a consistent UX experience.

Imperative workflow

We propose here an imperative workflow of managing haskell packages.

  1. the user asks cabal to install a package
  2. cabal makes this package visible to
    • ghc/ghci
    • cabal itself
  3. building a project (and similar operations) will re-use the already installed packages

This follows the workflow of 90% of Linux distributions. It usually disallows having multiple versions of the same library.

A sandbox workflow

The sandbox workflow is an extension of the imperative workflow. A sandbox is scoped to a specific directory (and its subdirectories). Installing packages inside the sandbox only makes them available within that directory. A sandbox can be conveniently created and conveniently destroyed. A user would rather destroy a sandbox and start from scratch rather than try to fix it. This is outlined in the following workflow:

  1. user clones e.g. copilot
  2. user creates a sandbox inside the copilot repo
  3. user installs the necessary Haskell libraries and the copilot library
  4. user uses a Makefile to link to copilot and other libraries (compare with bluespec, which does the same)
  5. user upgrades the project
  6. user destroys the sandbox
  7. repeats from step 2

A similar workflow can be imagined with an actual cabal package that is developed ad-hoc iteratively.

Users who want maximum caching and declarative configuration can use the existing -v2 interface instead.

Implementation details

We have considered the following:

  1. utilize -v1 to implement sandboxes (probably similar to the old implementation)
  2. utilize -v2 to implement sandboxes

v2 based sandbox implementation

A possible implementation would be:

  1. cabal sandbox init would e.g. augment cabal.project.local with several configurations
    • write-ghc-environment-files: always
    • store-dir: .cabal-sandbox (this doesn't seem to be supported yet through the cabal project file)
    • package-env: . (seems to be not supported either through cabal project file)
  2. cabal install would then
    • use the sandbox store dir
    • use the current dir as package env and write the configuration file
    • also add constraints: <pkg-name> installed to cabal.project.local
  3. cabal sandbox destroy would
    • delete the .cabal-sandbox directory
    • delete the ghc environment file
    • delete cabal.project.local

This would ensure consistency between directly invoking ghc and using cabal to actually build packages.

It would also allow to move the created constraints to cabal.project file instead. We could also imagine slight variants of this idea where we utilize yet another project file with a different suffix if cabal.project.local is deemed inappropriate.

Alternative v2 implementation without utilizing cabal.project files

An alternative implementation could create a sandbox config file that then triggers sandbox behavior in cabal (through cabal checking for its existence). This is partly implemented here: hasufell/cabal#3 But I consider it more controversial. However it is more opaque.

Related discussions and prior art

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