I was following the stack guide and I got a new project setup (yay!).
It generated the following file layout:
.
├── app
│ ├── Main.hs
├── .gitignore
├── LICENSE
├── helloworld.cabal
├── Setup.hs
├── src
│ └── Lib.hs
├── stack.yaml
└── test
└── Spec.hs
According to the "Files in helloworld" section of the guide:
The app/Main.hs, src/Lib.hs, and test/Spec.hs files are all Haskell source files that compose the actual functionality of our project (we won't dwell on them here).
I really wish they had dwelled on that for a second, because I have no idea what the distinction between app/Main.hs
and src/Lib.hs
should be. Which code should I put where?
In what ways am I supposed to divide code between app/
, src/
, app/Main.hs
and src/Lib.hs
?
If I'm just writing an application or just writing a library, do I need both files/directories?
Stack installs the Stackage libraries in ~/. stack and any project libraries or extra dependencies in a . stack-work directory within each project's directory.
stack build --skip <component> skips building the specified components of a local package. It allows you to skip test suites and benchmark without specifying other components (e.g. stack test --skip long-test-suite will run the tests without the long-test-suite test suite).
stack-work directories local to each project. The Stack root directory holds packages belonging to snapshots and any Stack-installed versions of GHC. Stack will not tamper with any system version of GHC or interfere with packages installed by other build tools (such as Cabal (the tool)).
You can find these sandboxed GHC installations in the ghc-* directories in the stack path --programs directory. If you would like Stack to use your system GHC installation, use the --system-ghc flag or run stack config set system-ghc --global true to make Stack check your PATH for a suitable GHC by default.
This separation of modules into folders can be any way you want. The naive idea is that you put almost all logic into the Lib
folder. Main.hs
then just
Lib
,You can rename app
into executables
and change the corresponding lines in .cabal
file. Actually, you can come up with an arbitrary file hierarchy.
In our company project, we use another but also very popular approach. And our file hierarchy looks like this:
.
|-- bench
|-- src
|-- exec1
|-- Main.hs
|-- exec2
|-- Main.hs
|-- SuperCoolLibrary
|-- LibModule1.hs
|-- LibModule2.hs
|-- test
|-- Setup.hs
Other stack.yaml
, .cabal
, etc. files are not shown here.
Actually, if you are writing an application, you can just create one Main.hs
file and put all logic inside the main
function. You won't believe it but as a Haskell lecturer I saw such code from my students :(
Though I don't suggest you write code that way.
If you are writing a library then you don't need Main.hs
files and the main
function at all. You can look at a simple example like this library (it allows you to automatically generate command-line options from data types): optparse-generic
I hope I helped clearing up your confusion.
The main reason it's typically set up like this even for an application is for writing tests. Say you create a default stack project called foo
, the test suite foo-test
will depend on the foo
library, as will the foo-exe
. If you were to put all your functions into app/Main.hs
, then those functions cannot be tested from the foo-test
test suite.
If you're just playing around and don't care about having a test suite, you could base your stack project on the simple
template:
$ stack new foo simple
If you'd like to set up testing, I like tasty
. You'd modify your .cabal
file something like this:
test-suite foo-test
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Spec.hs
build-depends: base
, foo
, tasty
, tasty-hunit
, tasty-quickcheck
ghc-options: -threaded -rtsopts -with-rtsopts=-N
default-language: Haskell2010
Then take a look at the example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With