hpack-dhall

Haskell package descriptions in Dhall.
This package named hpack-dhall
as described in
package.dhall
.
{ name =
"hpack-dhall"
...
, library =
{ exposed-modules = "Hpack.Dhall" }
, executables =
{ dhall-hpack-cabal = ...
, dhall-hpack-json = ...
, dhall-hpack-yaml = ...
, dhall-hpack-dhall = ...
}
}
This .cabal
creating executable can be run over its own package description;
> stack install --stack-yaml=stack-8.4.4.yaml
...
Copied executables to /.../hpack-dhall/__bin:
- dhall-hpack-cabal
- dhall-hpack-dhall
- dhall-hpack-json
- dhall-hpack-yaml
> __bin/dhall-hpack-cabal package.dhall
hpack-dhall.cabal is up-to-date
> __bin/dhall-hpack-cabal --force package.dhall
generated hpack-dhall.cabal
Using one of the golden tests for example, there are executables to show the
dhall with the imports made as well as json and yaml equivalents;
> __bin/dhall-hpack-dhall test/golden/hpack-dhall-cabal/empty-package.dhall
{ name = "empty-package" }
> __bin/dhall-hpack-json test/golden/hpack-dhall-cabal/empty-package.dhall
{
"name": "empty-package"
}
> __bin/dhall-hpack-yaml test/golden/hpack-dhall-cabal/empty-package.dhall
name: empty-package
By going from hpack package
fields to cabal package
properties,
we are not required to state what can be inferred or defaulted, easing the
burden of completing a package description by hand. For example
other-modules
can be inferred by taking the set difference between modules on
disk and the set of exposed-modules
.
By using an hpack-like Dhall dialect here rather than the
YAML of hpack we're able to;
- Add types to the fields.
- Safely import from other
*.dhall
files.
- Use functions.
Imports and Functions
With this safer and more capable alternative input format for hpack, we're able
to simply describe the package and by using imports and functions we can do
more such as configuring linting;
> cat default-extensions.dhall
{ default-extensions =
[ "DataKinds"
, "DeriveFunctor"
...
, "TupleSections"
, "UndecidableInstances"
]
}
> cat hlint.dhall
let Prelude/List/map =
https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/map
in let defs = ./default-extensions.dhall
in let f = λ(s : Text) → "-X" ++ s
in { arguments = Prelude/List/map Text Text f defs.default-extensions }
> dhall-to-yaml < ./hlint.dhall > ./.hlint.yaml
> cat .hlint.yaml
arguments:
- -XDataKinds
- -XDeriveFunctor
...
- -XTupleSections
- -XUndecidableInstances
We can pull those same default-extensions
into a package description;
> cat package.dhall
let defs = ./defaults.dhall
in defs
⫽ ./default-extensions.dhall
⫽ { name =
"flight-units"
...
, github =
"blockscope/flare-timing/units"
...
, dependencies =
defs.dependencies
# [ "numbers"
, "fixed"
, "bifunctors"
, "text"
, "formatting"
, "uom-plugin"
, "siggy-chardust"
]
...
}
We can consistently format package.dhall
and other *.dhall
imports using
dhall
;
> stack install dhall --stack-yaml=stack-dhall.yaml
> __bin/dhall format --inplace package.dhall
Restrictions
Using hpack's conditionals in
a list in package.dhall
can cause an error because lists in Dhall must have
elements of the same type;
From stack's package.yaml
;
executables:
stack:
main: Main.hs
source-dirs: src/main
generated-other-modules:
- Build_stack
- Paths_stack
ghc-options:
- -threaded
- -rtsopts
dependencies:
- stack
when:
- condition: flag(static)
ld-options:
- -static
- -pthread
- condition: ! '!(flag(disable-git-info))'
cpp-options: -DUSE_GIT_INFO
dependencies:
- githash
- optparse-simple
- condition: flag(hide-dependency-versions)
cpp-options: -DHIDE_DEP_VERSIONS
- condition: flag(supported-build)
cpp-options: -DSUPPORTED_BUILD
This can be represented in package.dhall
as;
, executables =
{ stack =
{ main =
"Main.hs"
, source-dirs =
[ "src/main" ]
, generated-other-modules =
[ "Build_stack", "Paths_stack" ]
, ghc-options =
[ "-threaded", "-rtsopts" ]
, dependencies =
[ "stack" ]
, when =
[ { condition =
"flag(static)"
, cpp-options =
[] : List Text
, dependencies =
[] : List Text
, ld-options =
[ "-static", "-pthread" ]
}
, { condition =
"!(flag(disable-git-info))"
, cpp-options =
[ "-DUSE_GIT_INFO" ]
, dependencies =
[ "githash", "optparse-simple" ]
, ld-options =
[] : List Text
}
, { condition =
"flag(hide-dependency-versions)"
, cpp-options =
[ "-DHIDE_DEP_VERSIONS" ]
, dependencies =
[] : List Text
, ld-options =
[] : List Text
}
, { condition =
"flag(supported-build)"
, cpp-options =
[ "-DSUPPORTED_BUILD" ]
, dependencies =
[] : List Text
, ld-options =
[] : List Text
}
]
}
}
Continuous Integration
With haskell-ci tooling installed, generate the .travis.yml
setup with;
> haskell-ci --output=.travis.yml --config=cabal.haskell-ci hpack-dhall.cabal
*INFO* Generating Travis-CI config for testing for GHC versions: 8.2.2 8.4.3 8.4.4 8.6.3