Tuesday, July 12, 2011

Cabal and GHCi

For my Summer of Code project, I'm giving cabal an repl command. At first, I thought a simple filename argument would be enough. More complex Cabal packages require a slightly more detailed input, however. Often times, there's more to a Cabal package than a library. Sometimes there's tests. Sometimes an executable. And all these components can have different ways of building themselves. This makes interpretation a bit of a problem. If a file is part of multiple components, which method of building should be used? Both should work, certainly, but choosing a component arbitrarily leaves little control to the user. In cases like this, it seems that cabal repl must take a component as well. Let's give it a flag --component=COMPONENT, where COMPONENT is the name of some library or executable or test suite that's a component in the cabal package. Fair enough.

But there's more. But what if a user wants to test a component in its entirety, not just one particular file? Here they only need to specify the component. So we say cabal repl --component=COMPONENT. What happens? What's loaded into GHCi? Should nothing be loaded, requiring the user to :load every module he wants to work with? Should everything be loaded, leading to potentially nasty name collisions? Should everything be loaded, but qualified, meaning that users will have to type hilariously long prefixes before all their functions? Should there be yet another flag for what course of action to take? Please, comment and tell me, because I have no idea.

6 comments:

  1. My uninformed "obviously this is how it should work" feeling is:

    (1) With no flags, cabal repl should load the library (there is at most one in a cabal file). If there isn't a library, I guess it could try the first executable, but you're really in "you ought to be telling me" territory then. When it comes to not wanting the library, I like the idea of a flag.

    (2) Note that modules can be loaded, but not imported. Probably nothing should be imported (so the prompt will just be Prelude> ...), but everything should be loaded. In the case of an executable, you may want to automatically import the "Main-is" module. Probably you don't want to provide another flag here, since it's easy enough to issue the appropriate ":m" or "import" statement from within GHCi.

    ReplyDelete
  2. Wow, I'm glad you're working on this! I've just started on a library that uses cabal-specific CPP conditionals, and frankly I have no idea how to use GHCi in developing it.

    ReplyDelete
  3. This will be great for Leksah. Currently we use "cabal build --with-gch=echo" to work out what command line options to pass to ghci (that might work for you jberryman). We are lazy and just use the first set of options returned (usually the library).

    Is it possible to use :set instead of passing the options on the command line? If so you might be able to :set the correct options before :loading stuff. Perhaps you could spit out a script for ghci that would :set, :unset and :load. This might also work for loading multiple cabal packages in.

    Not sure how :reload would handle that though. I wonder if it remembers the options that were :set when things were loaded.

    ReplyDelete
  4. Thanks for working on this.

    It would be interesting to hear how your work compares with the "cabal-dev ghci" command.

    ReplyDelete
  5. @yitzgale: cabal-dev will use cabal's repl support once it's available.

    In the meantime, I committed a patch to cabal-dev that implements the Leksah trick mentioned by @Hamish that improves the integration (now it will use the appropriate compiler options and have the same set of exposed packages as when cabal-dev builds it).

    ReplyDelete
  6. @Hamish: Only dynamic variables can be :set in ghci. Additionally, the arguments already given by cabal to ghc are so close to what ghci would be given that it's easiest to generate them the same way rather than going through a separate process of creating :set commands.

    @Chris: I like the ideas. Unless anyone objects, they're the ones I'll implement.

    ReplyDelete