10

Since things change so fast, I've posted this question so hopefully the community-agreed way to start a Haskell project can be clarified. Imagine I have two separate projects:

  • Project #1: Square, the library that squares numbers. No deps.

    -- Square.hs
    
    module Square where
    
    square :: Num a => a -> a
    square x = x * x
    
  • Project #2: Hypotenuse, the library and executable that finds the longest side of a right-angled triangle. Depends on #1:

    -- Hypotenuse.hs
    
    module Hypotenuse where
    
    import Square
    
    hypotenuse :: Floating a => a -> a -> a
    hypotenuse x y = sqrt $ square x + square y
    

    ,

    -- Main.hs
    
    import System.Environment
    import Hypotenuse
    
    main = do
        [x,y] <- fmap (map read) getArgs
        print $ hypotenuse x y
    

Starting with a computer with GHC 7.10.2, Stack and Cabal installed, and a single directory, ~/OrganizeMe, containing ~/OrganizeMe/Square.hs, ~/OrganizeMe/Hypotenuse.hs and ~/OrganizeMe/Main.hs, as presented above - what is a complete set of unix commands an experienced Haskeller would use to architect those projects? That includes:

  1. Organizing the directory tree of those projects;

  2. configuring Stack/Cabal/etc (and git, optionally);

  3. building/installing them locally;

  4. publishing to Hackage/Stackage.

MaiaVictor
  • 51,090
  • 44
  • 144
  • 286
  • I've read the whole documentation for Stack and some Cabal tutorials. Now I know about many features, but I still don't know how the directory trees are supposed to be organized. Do I need one directory for stack and multiple subdirectories for projects, each one with a `.cabal` file? Or what instead? How I can link import local projects from eachother? A hope solid example can clarify those and other questions. – MaiaVictor Oct 02 '15 at 13:41
  • Stack/Stackage is nice. has commercial backing and everything. – Erik Kaplun Oct 02 '15 at 13:48
  • 1
    On my mobile without much time, but you've got the right idea. One subdirectory per library, each with a cabal file, and reference each dir from the stack.yaml file. The wai repo provides a solid example of this – Michael Snoyman Oct 02 '15 at 14:27
  • Ah, that helps a lot. The only confusion left is the definition of "project", since wai is a single thing. Specifically, mind those two designs: 1. A single `.yaml` for all my personal packages. Example: `Viclib/viclib.yaml`, `Viclib/grid/grid.cabal`, `Viclib/bsp/bsp.cabal`. 2. One `.yaml` for each package, even if single file. Example: `Viclib/grid/grid.yaml`, `Viclib/grid/grid/grid.cabal`, `Viclib/bsp/bsp.yaml`, `Viclib/bsp/bsp/bsp.cabal`. What is the correct way? Looks like the second, but then how do I import `grid` from `bsp`, for example? – MaiaVictor Oct 02 '15 at 14:37

1 Answers1

4

This is not a complete answer, it does not start with your OrganizeMe directories (there are some errors in your code) and it does not include publishing to Hackage/Stackage. I start with a directory stackenv to contain both packages but you could do this quite differently of course.

mkdir stackenv && cd stackenv/
mkdir square && cd square
vim Square.hs # The file you describe without the x in the type of square
cabal init # Go through the questions and choose "library"
stack init
cd ../ && mkdir hypotenuse && cd hypotenuse/
vim Hypotenuse.hs # The file you describe
vim Main.hs # The file you describe but importing Hypotenuse
cabal init # Go through the questions... "executable" this time
stack init
vim hypotenuse.cabal # add "square" or chosen package name to build-depends
vim stack.yaml # add "- '../square/'" below packages
stack install
hypotenuse 3 4 # works because ~/.local/bin is in my PATH

Hope this helps.

Sam van Herwaarden
  • 2,321
  • 14
  • 27
  • Woops. I should've compiled the program before posting the question. Sorry about that. This looks reasonable to me, I'll wait more upvotes as a sign of consensus. Thank you. – MaiaVictor Oct 02 '15 at 17:03
  • Personally, I would recommend having `lib` and `bin` directories (or similar) under `hypotenuse`. This way you can use `hs-source-dirs` to make sure that the `Hypotenuse` module you import in the executable comes from the `hypotenuse` package rather than from the local `Hypotenuse.hs`, which cuts down on compile time. – Daniel Wagner Oct 03 '15 at 02:15
  • @DanielWagner: I'm not sure I understand. I normally have my code in a `src/` dir just for the organization. If you import `Hypotenuse` from `Hypotenuse.hs` it is only compiled if you change it, right? And the same holds if you import it from a package in stack? What is the conceptual difference between importing from `hypotenuse` or `Hypotenuse.hs`? – Sam van Herwaarden Oct 03 '15 at 06:20
  • By the way I'm still hoping someone will post a more complete answer (including the Hackage/Stackage part). Of course that should become the accepted answer then. – Sam van Herwaarden Oct 03 '15 at 06:22
  • 2
    @SamvanHerwaarden If you have all the files in the same directory, and change `Hypotenuse.hs`, it will be rebuilt twice: once for the library, and once for the executable. If you put the library in its own directory that is invisible to the executable, it will be rebuilt only once while rebuilding the library, then the library will be used in the executable. – Daniel Wagner Oct 03 '15 at 06:29
  • @DanielWagner do you mean a "bin" dir with `.hs` files? – MaiaVictor Oct 22 '15 at 17:35
  • 1
    @Viclib One standard layout is to have a `bin` directory for executables and a `lib` directory for libraries, yes. The important core thing is that the library's files be in a directory that is listed in `hs-source-dirs` for the library section but not the executable section(s). – Daniel Wagner Oct 22 '15 at 18:27
  • Thanks - is there anything wrong in having a top level directory with a `stack.yaml` file, and many directories inside it including `.cabal` files? – MaiaVictor Oct 24 '15 at 16:56