-
Notifications
You must be signed in to change notification settings - Fork 722
Description
I'd like to suggest some updates to the cabal file template used by cabal init
. We can discuss them separately.
Motivation
I believe that cabal should guide new projects towards writing the best Haskell code possible, without taking principled stances on things that are based in personal taste.
Disclaimer: My background is as a professional backend developer using Haskell, and I mentor new users in community spaces through their first cabal projects.
Current state of things
For the purpose of this ticket I created a new cabal project:
cabal-version: 3.4
name: lol
version: 0.1.0.0
-- synopsis:
-- description:
license: BSD-3-Clause
license-file: LICENSE
-- author:
-- maintainer:
-- copyright:
build-type: Simple
extra-doc-files: CHANGELOG.md
-- extra-source-files:
common warnings
ghc-options: -Wall
library
import: warnings
exposed-modules: MyLib
-- other-modules:
-- other-extensions:
build-depends: base ^>=4.18.3.0
hs-source-dirs: src
default-language: GHC2024
executable lol
import: warnings
main-is: Main.hs
-- other-modules:
-- other-extensions:
build-depends:
base ^>=4.18.3.0,
lol
hs-source-dirs: app
default-language: GHC2024
test-suite lol-test
import: warnings
default-language: GHC2024
-- other-modules:
-- other-extensions:
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Main.hs
build-depends:
base ^>=4.18.3.0,
lol
Changes
What does the project build?
$ cabal init
What does the package build:
1) Library
* 2) Executable
3) Library and Executable
4) Test suite
Your choice? [default: Executable]
A. Executable without library: The current community status quo encourages cabal projects to put most of their business logic in a library
component and put program initialisation in the executable
's Main.hs. As such, the executable
-only option does not reflect the best practices promoted by the community.
The fact that it is the default option makes it even worse.
My recommendation is to remove it.
B. Test suite: I can find a reasoning as to why some projects may want to have a separate package for their test suite that gets picked up by cabal test
, but highly it's unclear to beginners why they should care for such a thing. Moreover, the presence of Test suite
below Library and Executable
implies that the interactive wizard will not create a test suite component (since it's a different option).
My recommendation is to put an in-line explanation like (for projects who wish to separate their test suite)
if we are keeping it. I could see it removed, as it does not reflect a frequently used pattern for new projects.
Common stanzas
In the last industrial & hobby projects I have worked on, the default-language
is shared by all components. In fact, the current warnings
name is misleading because it incurs an artificial distinction between flags that provide warnings and those that do not. I would like to suggest (based on experience) a new nomenclature:
extensions
: Used by all components- `ghc-options: Used by all components
rts-options
: Used by allexecutable
,test
andbenchmark
components
In practice this looks like the following snippet (NOTE: The extensions and flags themselves are not suggested for inclusion in the template, they are just here as an example that I have picked verbatim from my projects):
common extensions
default-extensions:
DataKinds
DeepSubsumption
DeriveAnyClass
DerivingStrategies
DerivingVia
DuplicateRecordFields
GADTs
LambdaCase
NoStarIsType
NumericUnderscores
OverloadedLabels
OverloadedRecordDot
OverloadedStrings
PackageImports
PolyKinds
StrictData
TypeFamilies
UndecidableInstances
ViewPatterns
default-language: GHC2021
common ghc-options
ghc-options:
-Wall
-Wcompat
-Widentities
-Wincomplete-record-updates
-Wincomplete-uni-patterns
-Wpartial-fields
-fhide-source-paths
-Wno-unused-do-bind
-fshow-hole-constraints
-Wno-unticked-promoted-constructors
-Werror=unused-imports
-fdicts-strict
-fmax-worker-args=16
-fspec-constr-recursive=16
-Wunused-packages
-flate-specialise
-funbox-strict-fields
-finline-generics-aggressively
-fexpose-all-unfoldings
common rts-options
ghc-options:
-rtsopts
-threaded
"-with-rtsopts=-N -T"
You can see that GHC options used for both emitting warnings and controlling behaviour like strict dicts, unboxing, inlining, and UX improvements like -fhide-source-paths
are shared within one stanza, whereas the rtsopts relevant only for runtime are located elsewhere.
extra-doc-files, extra-source-files
It's good that CHANGELOG.md is included in extra-doc-files
, but I'd like to have a README.md generated and added to this stanza.
Bounds for GHC when selecting GHC language editions
I selected the GHC2024
language edition, which I'm told needs GHC 9.10 at least. However when generating the cabal file, the "default" GHC in my path (GHC 9.6) is used to generate the bounds of base, leading to a cabal-GHC error when I type cabal build
.
Since I (and most users) use GHCup to handle our toolchains, we are expected to use cabal.project
to give the appropriate GHC executable name. I'm not sure what the ideal workflow looks like here.
The cabal.project limbos
Ok I'm a GHCup user, I have asked on Discord how to make cabal pick my compiler of choice, I have learned that there exists a cabal.project
file that I can create (but never seen mentioned once in the guide or in the interactive wizard).
Turns out writing packages: ./
and with-compiler: ghc-9.10.1
is not enough because my test suite does not build when I run cabal build
or even cabal build all
. That's a big UX problem that is almost inexplicable because while the default is documented as False
, it's overridden when calling cabal test
.
My recommendations are:
-
Either we enable
tests: True
by default, which is the usually desired behaviour: If you have created a test suite and written tests, you usually want to build it with the rest, so thatcabal test
does not surprise you with a second build step just for your tests. -
We create a
cabal.project
file withpackages: ./
at least, and commented keys liketests
,documentation
andoptimization
. I'm specifically mentioningoptimization
because we want to discourage project authors to put optimisation levels in their package file, so the sooner we redirect them tocabal.project
, the better it will be.
I can do the implementation but please do judge these suggestions on their own merit.