Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where should the main module be kept in an idiomatic Haskell project?

Tags:

haskell

According to the wikipedia page for the Structure of a Haskell project, the src folder of an idiomatic haskell project looks as follows:

src/           -- For keeping the sourcecode
    Main.lhs     -- The main-module
    App/         -- Use hierarchical modules
      ...
      Win32/     -- For system dependent stuff
      Unix/
    cbits/       -- For C code to be linked to the haskell program

Questions:

  1. I'm assuming that the Main.lhs is the main entry point of the program. By that, I mean that it includes the main method which returns a value of type IO (a) for some type a. Is this the case?

  2. What is the meaning of the .lhs extension on Main.lhs? Why not call it Main.hs?

  3. I've looked at some other popular Haskell projects on GitHub and they do not seem to follow this structure that closely. Is this really the idiomatic way to organize a Haskell project?

like image 490
George Avatar asked Apr 30 '16 00:04

George


1 Answers

1.

Yes. The Haskell specification has a paragraph on this topic, and you have almost reproduced the language word for word.

2.

.lhs indicates that it's a literate Haskell file. In regular Haskell comments have to be marked off as special and code is the 'default'; in literate Haskell, the exact opposite is true: code has to be marked off as default and comments are the 'default.' As Knuth puts it, "The main idea is to regard a program as a communication to human beings rather than as a set of instructions to a computer."

3.

There is no de facto way to organize a Haskell project. I think there are several rough amorphous schools of thought:

  • The Blob. Throw everything into one directory and have Main.hs be the entry point. This is useful for one-off scripts and small projects. It makes for easy imports. You just import Foo and Foo.hs from the directory is included.

  • The Blob With Folders. Over time you realize that modules this and that belong together as submodules, and so do these and those. You now might create a src/ directory and throw all your files there. At this point you might start organizing like this:

    /
     src/
       • Main.hs
       • Types.hs
       Types/
         • Internal.hs
         • Gadgets.hs
         • Geegaws.hs
     • project.cabal
     • README.md
    

    Here we have a Types module that re-exports everything from Types.* so that you can import Types to get everything or, if you prefer, import Types.Internal or import Types.Gadgets to import the submodules of Types a la carte.

  • Big Library, Tiny Executable. If you use Cabal or Stack you can set up a library target and then set up an executable target that depends on the library target. The Main.hs and possible support modules live in the executable, and they import code from the library. This is ideal for testing, since tests are a separate target that can also depend on the library. You might also use this if you expect your project to sprout additional executable targets, which typifies larger systems. Your structure might look like this:

    /
     lib/
       Types/
         • Internal.hs
         • Gadgets.hs
         • Geegaws.hs
       Types.hs
     src/
       • Main.hs
       • Utils.hs
     • project.cabal
     • README.md
    

And there are all sorts of options in between. The lib/ and src/ naming is pretty idiomatic however. I'd guess that it comes from the way large C-family projects (especially GNU projects) tend to name things. cbits/ seems to be a fun name that everybody has collectively chosen to use for the directory that contains code that'll be foreign-function-interfaced into the Haskell code. It's OK to deviate from that wiki article is I guess the takeaway.

If you install Stack, you can create a directory and run stack new. Stack ships with a default directory structure (which you can customize or turn off entirely). Might be useful to compare and contrast.

like image 57
hao Avatar answered Oct 20 '22 10:10

hao