36

What is Haskell's syntax for importing modules in another directory?

I'm getting started with Haskell and want to practice writing simple functions TDD style with HUnit. I'm having trouble figuring out how to structure my files, though. The example that comes with HUnit seems to be a flat directory structure.

I'd like to have my tests and HUnit code in a different folder than my actual code. I'd appreciate a quick example import statement and a suggestion as to how I might structure my files.

If it matters, I'm using GHCi and NotePad++ to do my coding right now.

Mike Cluck
  • 31,869
  • 13
  • 80
  • 91
Josh Earl
  • 18,151
  • 15
  • 62
  • 91

2 Answers2

34

You don't actually do it from the Haskell source code; instead you tell the compiler where to look. The usual method is in the .cabal file. See the cabal user guide for details. You want the "hs-source-dirs" parameter.

Alternatively you can pass the path directly to the compiler. However, Cabal is the better method.

Each pathname in the "hs-source-dirs" parameter specifies a root of a module hierarchy. Basically if you import a module called "Data.Foo.Bar" then the compiler looks for a file with the relative pathname "Data/Foo/Bar.hs" in each directory given by "hs-source-dirs" and imports the first one it finds.

LambdaScientist
  • 435
  • 2
  • 13
Paul Johnson
  • 17,438
  • 3
  • 42
  • 59
  • 6
    Thanks, Paul. This was very helpful. I've of course seen a lot of discussion about Cabal, but I wasn't sure if that was the best alternative for simple projects. Like Linux, Haskell is a lot of fun but most people "explain" stuff at the 500 level and assume you have a lot of background already that beginners don't have. I appreciate the clarity. – Josh Earl Feb 18 '11 at 00:39
  • Is importing modules from hardcoded directories that aren't necessarily in the same package OK? – schuelermine Jun 29 '19 at 01:11
  • 1
    @schuelermine If you put the paths in the `hs-source-dirs` then by definition they become part of the package; they get tracked by cabal build, bundled up by cabal sdist etc. If you don't treat them as part of the package for version control purposes then you are going to get into a mess. In such a case your best bet is to make the other directories into one or more packages and then put the package path in `stack.yaml` `extra-deps` field https://docs.haskellstack.org/en/stable/yaml_configuration/ – Paul Johnson Jun 29 '19 at 08:24
20

Paul's answer is spot on, but I just wanted to expand on the idea of passing the path directly to the compiler for a quick and easy solution, mainly for running scripts with runhaskell or runghc.

All you need to do is pass the -i flag to the compiler with a colon-delimited list of directories. The compiler will then check those directories for the source files of the imported modules.

So, for example, if you have directory structure like so:

home/
|-- user/
    |-- haskell/
        |-- Module1.hs
        |-- foo/
            |-- Module2.hs

And you would like Module2 to be able to import Module1, then inside Module2.hs add your import statement as usual:

import Module1

Then when you execute Module2.hs using runhaskell you would run it like so:

$ cd /home/user/haskell/foo
$ runhaskell -i/home/user/haskell Module2.hs

Reference:

Philippe Fanaro
  • 6,148
  • 6
  • 38
  • 76
J.C. Yamokoski
  • 1,014
  • 9
  • 23
  • Thanks for the reference link, it took me as long to find it as it did to find this answer. `runhaskell --help` punts by showing `[runghc flags] [GHC flags]` in the usage, and `runghc --help` says... exactly the same thing. And finally `ghc --show-options` shows us `-i`, but with no description on the CLI. One ultimately has to make it to the web page you referenced. Seems roundabout for what must be a pretty common case in early/simple tests like OP's situation, but I suppose that's why Cabal is the next progressive step. – ches Jan 31 '15 at 06:53
  • how search-path from above reference is related with https://downloads.haskell.org/~ghc/7.0.3/docs/html/users_guide/syntax-extns.html#hierarchical-modules ? Is it the same ? – Adam Aug 03 '17 at 18:33