Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How am I meant to split code between src/Lib.hs and app/Main.hs in a new stack project?

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?

like image 937
Sunjay Varma Avatar asked Aug 21 '16 23:08

Sunjay Varma


People also ask

Where does stack install packages?

Stack installs the Stackage libraries in ~/. stack and any project libraries or extra dependencies in a . stack-work directory within each project's directory.

What does stack build do?

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).

How does Stack-work in Haskell?

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)).

Where does stack install GHC?

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.


2 Answers

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.

like image 158
Shersh Avatar answered Oct 01 '22 16:10

Shersh


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.

like image 37
Steven Shaw Avatar answered Oct 01 '22 17:10

Steven Shaw