Re-design the ProgramDb abstraction to avoid accidentally dropping unconfigured programs
- Dominant language
- Haskell
- Stars
- 1.7k
- Forks
- 750
- Avg merge
- 4d 3h
- Merged PRs (30d)
- 28
Description
We have
```haskell
data ProgramDb = ProgramDb
{ unconfiguredProgs :: UnconfiguredProgs
, progSearchPath :: ProgramSearchPath
, configuredProgs :: ConfiguredProgs
}
-- | Note that this instance does not preserve the known 'Program's.
-- See 'restoreProgramDb' for details.
instance Binary ProgramDb where
put db = do
put (progSearchPath db)
put (configuredProgs db)
get = do
searchpath <- get
progs <- get
return $!
emptyProgramDb
{ progSearchPath = searchpath
, configuredProgs = progs
}
```
Note here that the `Binary` instance for `ProgramDb` discards the unconfigured programs. This is because arbitrary functions can be stored in that field (e.g. `programFindVersion :: Verbosity -> FilePath -> IO (Maybe Version)`).
However, this easily leads to bugs; #2241 was an example. I ran into another bug today, which was that the `configureCompiler` function in `cabal-install` would also drop unconfigured programs, which could lead to e.g. failing to find the `ar` executable (`The program 'ar' is required but it could not be found.`).
One first step which could help avoiding bugs is to define a newtype around `ProgramDb` and attach the `Binary` instance to that newtype instead. The wrapping/unwrapping of the newtype would hopefully introduce the appropriate amount of ceremony to avoid accidents.
More ambitiously, we could re-design `ProgramDb`, e.g. by introducing a serialisable DSL for unconfigured programs, or perhaps by using static pointers.
Contributor guide
Assessment
This issue has not been assessed yet.