How am I meant to split code between src/Lib.hs and app/Main.hs in a new stack project?
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
- imports required parts from
Lib
, - reads command-line arguments, and
- runs stuff.
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.