Search code examples
haskellcabal

Compiling a haskell script with external dependencies without cabal


I'm relatively new to Haskell and I realize I might be swimming against the stream here, but nonetheless, I'll ask:

Say I have a short Haskell script:

import Data.List.Split (splitOn)

main :: IO ()
main = do
  let orders = splitOn "x" "axbxc"
  putStrLn $ head orders

If I used only standard functions I could compile this with ghc <script.hs>. Because I depend on the split package to provide the splitOn function, the compilation fails.

Now, I have no difficulties setting up a cabal project with a project.cabal and a Setup.hs file in order to get this to actually compile. However, this feels like a lot of extra boilerplate for a standalone script.

So, is there a way to compile a single .hs file against some external package? Something similar to what in Python would be done by pip install something, "installing the package into the interpreter", i.e. is there a way to install extra packages "into ghc", so that I for instance only need to provide some extra linking flag to ghc?


Solution

  • If you use Stack, the simplest way to do this is to write a ‘Stack script’, which is a Haskell file with a description of the required packages in the first line (really an invocation of stack specifying the appropriate command line arguments). An example (slightly modified from the docs):

    $ cat turtle-example.hs
    -- stack --resolver lts-6.25 script --package turtle
    {-# LANGUAGE OverloadedStrings #-}
    import Turtle
    main = echo "Hello World!"
    $ stack ./turtle-example.hs
    Completed 5 action(s).
    Hello World!
    $ stack ./turtle-example.hs
    Hello World!
    

    This script uses the turtle package; when run, Stack downloads and builds this dependency, after which it is available in the script. (Note that the second time it is run, turtle has already been built so does not need to be rebuilt again.)

    As it happens, the --package command in Stack is not limited to scripts. It can be used with other Stack commands as well! For instance, to compile your program, you should be able to run stack ghc --resolver lts-16.27 --package split -- -ghc-options your-program-name.hs. And stack ghci --package split will give you a GHCi prompt where you can import Data.List.Split.

    (Note: This answer focuses on Stack rather than Cabal, simply because I don’t know Cabal very well. However, I believe all this can be done using Cabal as well. For instance, I do know that Cabal has something very similar to the Stack scripts I mentioned above, though I can’t remember the syntax just at the moment.)

    EDIT: See @duplode’s answer for how to do this with Cabal.