HORIZON HASKELLDocslts/ghc-9.10.xc74966e2026-09-27Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · c74966e · 2026-09-27

Modulecabal-install-3.12.1.0Haskell2010

Distribution.Client.RebuildMonad

An abstraction for re-running actions if values or files have changed.

This is not a full-blown make-style incremental build system, it's a bit more ad-hoc than that, but it's easier to integrate with existing code.

It's a convenient interface to the Distribution.Client.FileMonitor functions.

  • 7 types
  • 29 values

Rebuild monad

4 declarations
newtypenewtype Rebuild a
#

A monad layered on top of IO to help with re-running actions when the input files and values they depend on change. The crucial operations are rerunIfChanged and monitorFiles.

Instances4Monad, Functor, Applicative, MonadIO
  • Monad RebuildDefined in cabal-install-3.12.1.0 · Distribution.Client.RebuildMonad
  • Functor RebuildDefined in cabal-install-3.12.1.0 · Distribution.Client.RebuildMonad
  • Applicative RebuildDefined in cabal-install-3.12.1.0 · Distribution.Client.RebuildMonad
  • MonadIO RebuildDefined in cabal-install-3.12.1.0 · Distribution.Client.RebuildMonad

Setting up file monitoring

11 declarations

Use this within the body action of rerunIfChanged to declare that the action depends on the given files. This can be based on what the action actually did. It is these files that will be checked for changes next time rerunIfChanged is called for that FileMonitor.

Relative paths are interpreted as relative to an implicit root, ultimately passed in to runRebuild.

datadata MonitorFilePath
#

A description of a file (or set of files) to monitor for changes.

Where file paths are relative they are relative to a common directory (e.g. project root), not necessarily the process current directory.

Instances6Eq, Show, Generic, Binary, Structured, Rep

Monitor a single file for changes, based on its modification time. The monitored file is considered to have changed if it no longer exists or if its modification time has changed.

Monitor a single file for changes, based on its modification time and content hash. The monitored file is considered to have changed if it no longer exists or if its modification time and content hash have changed.

Monitor a single directory for changes, based on its modification time. The monitored directory is considered to have changed if it no longer exists or if its modification time has changed.

Monitor a single file or directory for changes, based on its modification time. The monitored file is considered to have changed if it no longer exists or if its modification time has changed.

Monitoring file globs

Monitor a set of files (or directories) identified by a file glob. The monitored glob is considered to have changed if the set of files matching the glob changes (i.e. creations or deletions), or for files if the modification time and content hash of any matching file has changed.

Monitor a set of files (or directories) identified by a file glob for existence only. The monitored glob is considered to have changed if the set of files matching the glob changes (i.e. creations or deletions).

datadata RootedGlob
#

A file path specified by globbing, relative to some root directory.

Instances8Eq, Show, Generic, Binary, Parsec, Pretty, …
datadata FilePathRoot
#

Constructors

Instances8Eq, Show, Generic, Binary, Parsec, Pretty, …
datadata Glob
#

A filepath specified by globbing.

Constructors

Instances8Eq, Show, Generic, Binary, Parsec, Pretty, …
datadata GlobPiece
#

A piece of a globbing pattern

Constructors

Instances6Eq, Show, Generic, Binary, Structured, Rep

Using a file monitor

3 declarations
datadata FileMonitor a b
#

A monitor for detecting changes to a set of files. It can be used to efficiently test if any of a set of files (specified individually or by glob patterns) has changed since some snapshot. In addition, it also checks for changes in a value (of type a), and when there are no changes in either it returns a saved value (of type b).

The main use case looks like this: suppose we have some expensive action that depends on certain pure inputs and reads some set of files, and produces some pure result. We want to avoid re-running this action when it would produce the same result. So we need to monitor the files the action looked at, the other pure input values, and we need to cache the result. Then at some later point, if the input value didn't change, and none of the files changed, then we can re-use the cached result rather than re-running the action.

This can be achieved using a FileMonitor. Each FileMonitor instance saves state in a disk file, so the file for that has to be specified, making sure it is unique. The pattern is to use checkFileMonitorChanged to see if there's been any change. If there is, re-run the action, keeping track of the files, then use updateFileMonitor to record the current set of files to monitor, the current input value for the action, and the result of the action.

The typical occurrence of this pattern is captured by rerunIfChanged and the Rebuild monad. More complicated cases may need to use checkFileMonitorChanged and updateFileMonitor directly.

Constructors

valuenewFileMonitor
  1. :: Eq a
  2. => FilePath

    The file to cache the state of the file monitor. Must be unique.

  3. -> FileMonitor a b
#

Define a new file monitor.

It's best practice to define file monitor values once, and then use the same value for checkFileMonitorChanged and updateFileMonitor as this ensures you get the same types a and b for reading and writing.

The path of the file monitor itself must be unique because it keeps state on disk and these would clash.

valuererunIfChanged
  1. :: (Binary a, Structured a, Binary b, Structured b)
  2. => Verbosity
  3. -> FileMonitor a b
  4. -> a
  5. -> Rebuild b
  6. -> Rebuild b
#

This captures the standard use pattern for a FileMonitor: given a monitor, an action and the input value the action depends on, either re-run the action to get its output, or if the value and files the action depends on have not changed then return a previously cached action result.

The result is still in the Rebuild monad, so these can be nested.

Do not share FileMonitors between different uses of rerunIfChanged.

Utils

12 declarations

When using rerunIfChanged for each element of a list of actions, it is sometimes the case that each action needs to make use of some resource. e.g.

sequence
  [ rerunIfChanged verbosity monitor key $ do
      resource <- mkResource
      ... -- use the resource
  | ... ]

For efficiency one would like to share the resource between the actions but the straightforward way of doing this means initialising it every time even when no actions need re-running.

resource <- mkResource
sequence
  [ rerunIfChanged verbosity monitor key $ do
      ... -- use the resource
  | ... ]

This utility allows one to get the best of both worlds:

getResource <- delayInitSharedResource mkResource
sequence
  [ rerunIfChanged verbosity monitor key $ do
      resource <- getResource
      ... -- use the resource
  | ... ]

Utility to match a file glob against the file system, starting from a given root directory. The results are all relative to the given root.

Since this operates in the Rebuild monad, it also monitors the given glob for changes.

valueneedIfExists :: FilePath -> Rebuild ()
#

Monitor a file if it exists; otherwise check for when it gets created. This is a bit better for recompilation avoidance because sometimes users give bad package metadata, and we don't want to repeatedly rebuild in this case (which we would if we need'ed a non-existent file).