In Haskell, while using stack, there are three places where we can define dependencies on the package.yaml file; under library and executables. What is the difference between defining a dependency in these places and when should I use them?
CodePudding user response:
Let's say you want to build and application with two front ends: CLI and a GUI, both using the same underliying logic. Obviously, your CLI doesn't use any graphical libraries, so It shouldn't depend on them and viceversa. Your package.yaml will look something like.
name: my-app
version: 0.1.0.0
# This are the dependencies all the components depends on.
dependencies:
- base >= 4.7 && < 5
# This is the description of your "common logic" i.e. the library
library:
source-dirs: src
# depends on whatever libraries of your choice
dependencies:
- bytestring
- megaparsec
- parser-combinators
- transformers
executables:
# The executable name for the cli
my-app-cli:
# The main function is in cli.hs
main: cli.hs
source-dirs: app
ghc-options:
- -Wall
- -Wextra
- -threaded
- -rtsopts
- -with-rtsopts=-qg
# It depends on you library, the base library (which remember is at the top of the file), and on whatever other libraries you use to build the cli, for example optparse-applicative
dependencies:
- my-app
- optparse-applicative
# The executable name for the GUI
my-app-gui:
# The main function is on gui.hs
main: gui.hs
source-dirs: app
ghc-options:
- -Wall
- -Wextra
- -threaded
- -rtsopts
- -with-rtsopts=-qg
# Again, It depends on your library, the base, and a GUI library of your choice. Example monomer.
dependencies:
- my-app
- monomer
Now when you run stack build it will create two executables, one named my-app-cli and other my-app-gui, each with its own dependencies, but sharing the common ones. Ideally one could run stack build my-app:my-app-cli to build just one of the executables, but for some reason stack builds everything (which apparently is due to some cabal behaviour... don't know, don't care)
That been said, I don't think this is different from other programming languages. For example, I tend to structure my Python code the same way. A common library with its own requirements.txt and then different applications each with its own requirement.txt depending if the application is gonna be a web server, a machine learning model or what ever... this simplifies creating cached layer in docker for example. If I need to add some dependencies to the web server, they are added to the web server requirements.txt instead of the library one. This is, of course opinionated but my point is that this isn't haskell specific.
