HORIZON HASKELLDocslts/ghc-9.14.x3e2d5682026-10-11Search names, modules, packages, or :: a typeCtrl K

GHC 9.14.1 · lts/ghc-9.14.x · 3e2d568 · 2026-10-11

Modulehorizon-config-0.8.0GHC2024

Horizon.Config.Effects

The package-set schema, as an effect row. Each operation is one kind of declaration a config may make. The row has no IO and no lifting — a config script is capability-free by construction, so the type checker guarantees it can only describe a package set, never perform an effect. This is what makes it a safe config language.

Each pin argument carries a validating newtype from Horizon.Config.Types rather than a bare Text, so a malformed literal (a revision that is not a hash, a version that is not numeric) is a fold-time error. With OverloadedStrings a config still writes bare string literals — GHC picks the right fromString per argument — so the .vx surface is unchanged.

  • 2 types
  • 43 values
datadata Spec (m :: Type -> Type) a where
#
  • Compiler :: Compiler -> Spec m ()compiler

    Set the GHC compiler, e.g. compiler "ghc-9.10.1".

  • On :: Base -> Spec m ()on

    What the set is built on, exactly once: on (toolchain (forge "nixlab.openfp.netpackage-setshaskellhorizon-build-packages") (branch "ltsghc-10.0.x")) for a set built from scratch with a toolchain's compiler and build tools, or on (set "horizon-core" (forge …) (branch …)) for an overlay on a lower package set, which the set takes its compiler from and whose packages it solves around at the versions that set holds. The base is the generated flake.nix's one real input; the branch is what it follows, and the flake lock records the commit. A config with no base is an overlay for an application, which writes no flake.

  • Platform :: Platform -> Spec m ()platform

    The platform the set is rendered for, as cabal names it: its packages' arch(…) and os(…) conditionals are resolved for this target rather than for the machine that runs the generator, e.g. platform "javascript-ghcjs" for a set built by the JavaScript backend. Undeclared, a set is rendered for x86_64-linux, whichever machine generates it.

  • IndexState :: IndexState -> Spec m ()indexState

    The Hackage index state the set is read at, e.g. indexState "2025-03-01T00:00:00Z": each Hackage pin's revision is the last one published by then, and a Solve against hackage chooses among the releases Hackage had then, so nothing published later changes the generated set. Undeclared, the newest the index the generator reads holds.

  • Name :: Text -> Spec m ()name

    The set's name (used for the generated module + flake description), e.g. name "horizon-build-essentials".

  • Description :: Text -> Spec m ()description

    A one-line description of the set.

  • License :: Spdx -> Spec m ()license

    The licence, as an SPDX identifier: license MIT.

  • Warnings :: Warnings -> Spec m ()warnings

    The warnings contract: warnings "werrorwolf", every package also built as a check with -Werror; warnings "none" for none.

  • Language :: Language -> Spec m ()language

    The default language of the local packages: language GHC2024.

  • Extension :: Extension -> Spec m ()extension

    A default extension of the local packages, once per verb: extension OverloadedStrings.

  • NixConfig :: Text -> Text -> Spec m ()nixConfig

    A nixConfig setting of the generated flake, key and value: nixConfig "extra-substituters" "https://cache.openfp.net".

  • DoBenchmark :: Bool -> Spec m ()doBenchmark
  • DoCheck :: Bool -> Spec m ()doCheck
  • DoHaddock :: Bool -> Spec m ()doHaddock
  • DoJailbreak :: Bool -> Spec m ()doJailbreak
  • EnableProfiling :: Bool -> Spec m ()enableProfiling
  • IncludeBenchmarks :: Bool -> Spec m ()includeBenchmarks
  • IncludeExecutables :: Bool -> Spec m ()includeExecutables
  • IncludeTests :: Bool -> Spec m ()includeTests
  • Hackage :: PackageName -> Version -> Spec m ()hackage

    A package from Hackage at a pinned version.

  • Repository :: RepositoryName -> Url -> Int -> [RootKey] -> Spec m ()repository

    Declare a secure cabal repository the set pins packages from, as a cabal repository stanza does: its name, its URL, how many of the root keys must sign its root metadata, and the root key ids, e.g. repository "nixlab" "https://nixlab.openfp.net/hackage" 2 ["…", "…", "…"]. The generator fetches its index with cabal, which checks the repository's signatures against these keys. A repository is declared before the pins that name it.

  • HackageFrom :: RepositoryName -> PackageName -> Version -> Spec m ()hackageFrom

    A package at a pinned version from a declared repository instead of Hackage: repository, name, version, e.g. hackageFrom "nixlab" "vortex-trace" "0.3.0".

  • Git :: PackageName -> Url -> Revision -> Spec m ()git

    A package from a git repository at a revision.

  • GitSubdir :: PackageName -> Url -> Revision -> Subdir -> Spec m ()gitSubdir

    The same, from a subdirectory of the repository: name, url, revision, subdirectory.

  • Tarball :: PackageName -> Url -> Spec m ()tarball

    A package from a tarball URL: name, url.

  • TarballSubdir :: PackageName -> Url -> Subdir -> Spec m ()tarballSubdir

    The same, from a subdirectory of the unpacked tarball.

  • Local :: PackageName -> Subdir -> Spec m ()local

    A LOCAL package: its source is a subdirectory of this config's own repo, not a fetched artifact — so a set can include a project developed alongside it (the code you are writing here): name, subdirectory.

  • EnableFlag :: PackageName -> FlagName -> Spec m ()enableFlag

    Turn a cabal flag on for a declared package: package, flag.

  • DisableFlag :: PackageName -> FlagName -> Spec m ()disableFlag

    Turn a cabal flag off for a declared package: package, flag.

  • Patch :: PackageName -> Text -> Spec m ()patch

    Apply a source patch to a declared package: package, path to the .patch/.diff file (which stays raw in the repo). A genuinely per-package fact — the diff is specific to that package's source.

  • SystemDeps :: PackageName -> Text -> Spec m ()systemDeps

    A native (system) library a declared package binds, injected into its derivation as callPackage … { lib = pkgs.lib; }: package, library name, e.g. systemDeps "zlib" "zlib". Per-package by nature (a fact about that package's FFI), not set policy.

  • SystemDepFrom :: PackageName -> Text -> Text -> Spec m ()systemDepFrom

    A native library whose callPackage argument is named differently from the nixpkgs attribute that supplies it: package, argument, attribute, e.g. systemDepFrom "cardano-crypto-class" "libblst" "blst".

  • Exposed :: PackageName -> ModuleName -> Spec m ()exposed

    A module a local package exposes: package, module, e.g. exposed "tint" Tint.Diff; once per module.

  • Internal :: PackageName -> ModuleName -> Spec m ()internal

    A module a local package keeps internal (other-modules).

  • Suite :: PackageName -> SuiteName -> MainIs -> Spec m ()suite

    A test suite of a local package: package, suite, the file it runs, e.g. suite "tint" "laws" "Laws.hs".

  • Backend :: PackageName -> Backend -> Spec m ()backend

    A backend a local package builds for, once per backend: backend "tint" "javascript". Undeclared, the native one.

  • Missing :: PackageName -> Spec m ()missing

    Assert that a package is intentionally NOT provided by this set — an architecture gap, no source, or deliberately unavailable. This is the ONLY named absence (never a bare = null): the generator records it, and if a root transitively needs a missing package that is a hard error rather than a silent hole. (Packages that are merely unreached under the set's policy are pruned, not declared missing.)

  • Overlay :: Text -> Spec m ()overlay

    Export an overlay of the declared packages to this file, e.g. overlay "overlay.nix". The default: what a library or an application declares are the packages its base set is missing.

  • PackageSet :: Text -> Spec m ()packageSet

    Export a whole package set to this file instead, e.g. packageSet "initial-packages.nix", for a repository that builds a package set from scratch.

  • Root :: PackageName -> Spec m ()root

    Keep a package in the set for its own sake: the set exists to provide it, not because something else in the set needs it. The roots and everything they depend on are what the set must carry; any other declaration is a pin nothing reaches, which horizon-config --prune reports so it can be removed.

  • Index :: Text -> Spec m ()index

    Declare a cabal index to resolve against, e.g. index "hackage" or index "head.hackage". Declaration order is precedence (earlier = higher): when a package is in several indices the resolved one comes from the highest-precedence index carrying a usable version. Named indices resolve to known repositories (hackage, head.hackage, and chap for Cardano Haskell Packages) or to a repository the config declares with Repository. Only meaningful with Solve.

  • AllowNewer :: PackageName -> Spec m ()allowNewer

    Relax the upper version bounds on a package during the solve, e.g. allowNewer "base" — the solve-time counterpart of a build-time jailbreak. Needed when a bleeding-edge compiler ships a boot library (base, template-haskell) newer than dependants' stated bounds. Changes what the solver may pick, so it is declared, not assumed. Only meaningful with Solve.

  • Solve :: Spec m ()solve

    Expand the declared roots into the full transitive closure by running the solver against the declared indices, writing the resolved closure to the lock. Explicit: a config without solve uses its literal hackage/git pins as written. A set has one closure, so there is one solve.

typetype Config = Sem '[Spec]
#

What a config is: one expression in a pure declaration row. No IO appears in the type, so a config cannot escape into the world; the host folds it into a value.

valueon :: Member Spec r => Base -> Sem r ()
#

What the set is built on, exactly once: on (toolchain (forge "nixlab.openfp.netpackage-setshaskellhorizon-build-packages") (branch "ltsghc-10.0.x")) for a set built from scratch with a toolchain's compiler and build tools, or on (set "horizon-core" (forge …) (branch …)) for an overlay on a lower package set, which the set takes its compiler from and whose packages it solves around at the versions that set holds. The base is the generated flake.nix's one real input; the branch is what it follows, and the flake lock records the commit. A config with no base is an overlay for an application, which writes no flake.

valueplatform :: Member Spec r => Platform -> Sem r ()
#

The platform the set is rendered for, as cabal names it: its packages' arch(…) and os(…) conditionals are resolved for this target rather than for the machine that runs the generator, e.g. platform "javascript-ghcjs" for a set built by the JavaScript backend. Undeclared, a set is rendered for x86_64-linux, whichever machine generates it.

valueindexState :: Member Spec r => IndexState -> Sem r ()
#

The Hackage index state the set is read at, e.g. indexState "2025-03-01T00:00:00Z": each Hackage pin's revision is the last one published by then, and a Solve against hackage chooses among the releases Hackage had then, so nothing published later changes the generated set. Undeclared, the newest the index the generator reads holds.

valuename :: Member Spec r => Text -> Sem r ()
#

The set's name (used for the generated module + flake description), e.g. name "horizon-build-essentials".

valuewarnings :: Member Spec r => Warnings -> Sem r ()
#

The warnings contract: warnings "werrorwolf", every package also built as a check with -Werror; warnings "none" for none.

valuerepository
  1. :: Member Spec r
  2. => RepositoryName
  3. -> Url
  4. -> Int
  5. -> [RootKey]
  6. -> Sem r ()
#

Declare a secure cabal repository the set pins packages from, as a cabal repository stanza does: its name, its URL, how many of the root keys must sign its root metadata, and the root key ids, e.g. repository "nixlab" "https://nixlab.openfp.net/hackage" 2 ["…", "…", "…"]. The generator fetches its index with cabal, which checks the repository's signatures against these keys. A repository is declared before the pins that name it.

valuelocal :: Member Spec r => PackageName -> Subdir -> Sem r ()
#

A LOCAL package: its source is a subdirectory of this config's own repo, not a fetched artifact — so a set can include a project developed alongside it (the code you are writing here): name, subdirectory.

valuepatch :: Member Spec r => PackageName -> Text -> Sem r ()
#

Apply a source patch to a declared package: package, path to the .patch/.diff file (which stays raw in the repo). A genuinely per-package fact — the diff is specific to that package's source.

valuesystemDeps :: Member Spec r => PackageName -> Text -> Sem r ()
#

A native (system) library a declared package binds, injected into its derivation as callPackage … { lib = pkgs.lib; }: package, library name, e.g. systemDeps "zlib" "zlib". Per-package by nature (a fact about that package's FFI), not set policy.

valuesystemDepFrom :: Member Spec r => PackageName -> Text -> Text -> Sem r ()
#

A native library whose callPackage argument is named differently from the nixpkgs attribute that supplies it: package, argument, attribute, e.g. systemDepFrom "cardano-crypto-class" "libblst" "blst".

valuemissing :: Member Spec r => PackageName -> Sem r ()
#

Assert that a package is intentionally NOT provided by this set — an architecture gap, no source, or deliberately unavailable. This is the ONLY named absence (never a bare = null): the generator records it, and if a root transitively needs a missing package that is a hard error rather than a silent hole. (Packages that are merely unreached under the set's policy are pruned, not declared missing.)

valueoverlay :: Member Spec r => Text -> Sem r ()
#

Export an overlay of the declared packages to this file, e.g. overlay "overlay.nix". The default: what a library or an application declares are the packages its base set is missing.

valuepackageSet :: Member Spec r => Text -> Sem r ()
#

Export a whole package set to this file instead, e.g. packageSet "initial-packages.nix", for a repository that builds a package set from scratch.

valueroot :: Member Spec r => PackageName -> Sem r ()
#

Keep a package in the set for its own sake: the set exists to provide it, not because something else in the set needs it. The roots and everything they depend on are what the set must carry; any other declaration is a pin nothing reaches, which horizon-config --prune reports so it can be removed.

valueindex :: Member Spec r => Text -> Sem r ()
#

Declare a cabal index to resolve against, e.g. index "hackage" or index "head.hackage". Declaration order is precedence (earlier = higher): when a package is in several indices the resolved one comes from the highest-precedence index carrying a usable version. Named indices resolve to known repositories (hackage, head.hackage, and chap for Cardano Haskell Packages) or to a repository the config declares with Repository. Only meaningful with Solve.

valueallowNewer :: Member Spec r => PackageName -> Sem r ()
#

Relax the upper version bounds on a package during the solve, e.g. allowNewer "base" — the solve-time counterpart of a build-time jailbreak. Needed when a bleeding-edge compiler ships a boot library (base, template-haskell) newer than dependants' stated bounds. Changes what the solver may pick, so it is declared, not assumed. Only meaningful with Solve.

valuesolve :: Member Spec r => Sem r ()
#

Expand the declared roots into the full transitive closure by running the solver against the declared indices, writing the resolved closure to the lock. Explicit: a config without solve uses its literal hackage/git pins as written. A set has one closure, so there is one solve.