cabal init

April 15, 2010

During the Hackathon in Edinburgh last year, Benedikt Huber and I worked hard on adding a new feature to cabal-install. It eventually made its way into the codebase and has now been a part of cabal-install since version 0.8.0. However, it seems that lots of people still don’t know about it, so I thought I’d write a quick post about it.

What do you do when you want to create a new Cabalized package? If you’re like me, in the past you’ve probably copy-and-pasted the .cabal file from a different project and edited it. And the first .cabal file you ever made, you probably copy-and-pasted from someone else’s project. This works fine up to a point, but it’s annoying, and you never end up learning about great new features that Cabal now supports, or about things you’ve been doing that are now deprecated. Wouldn’t it be great if the initial setup for a Cabal project could be generated for you?

The solution is a new mode for the cabal command line tool, cabal init, which interactively prompts you for some information and creates a default .cabal file and related package stuff for you. Without further ado, let’s see it in action!

$ mkdir myproject
$ cd myproject
$ cat > Foo.hs
module Foo where

x = 3

This is going to be the best package ever!

$ cabal init
Package name [default "myproject"]?           
Package version [default "0.1"]? 
Please choose a license:
   1) GPL
   2) GPL-2
   3) GPL-3
   4) LGPL
   5) LGPL-2.1
   6) LGPL-3
 * 7) BSD3
   8) BSD4
   9) MIT
  10) PublicDomain
  11) AllRightsReserved
  12) OtherLicense
  13) Other (specify)
Your choice [default "BSD3"]? 
Author name [default "Brent Yorgey"]? 
Maintainer email [default "byorgey@cis.upenn.edu"]? 
Project homepage/repo URL? http://best.project.evar/
Project synopsis? The best package ever!  Defines 3 so you don't have to.
Project stability:
   1) Stable
   2) Provisional
 * 3) Experimental
   4) Alpha
   5) Other (specify)
Your choice [default "Experimental"]? 
Project category:
   1) Codec
   2) Concurrency
   3) Control
   4) Data
   5) Database
   6) Development
   7) Distribution
   8) Game
   9) Graphics
  10) Language
  11) Math
  12) Network
  13) Sound
  14) System
  15) Testing
  16) Text
  17) Web
  18) Other (specify)
Your choice? 11
What does the package build:
   1) Library
   2) Executable
Your choice? 1
Generating LICENSE...
Generating Setup.hs...
Generating myproject.cabal...

You may want to edit the .cabal file and add a Description field.

Notice how it automatically guessed the project name and my name and email. (Actually, I’m not even sure how it did that! Fancy!) And here’s what it generated. Notice that the module Foo is listed as an export; it automatically looks through the current directory and any subdirectories for things that look like modules to be exported.

$ cat myproject.cabal
-- myproject.cabal auto-generated by cabal init. For additional
-- options, see
-- http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr.
-- The name of the package.
Name:                myproject

-- The package version. See the Haskell package versioning policy
-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for
-- standards guiding when and how versions should be incremented.
Version:             0.1

-- A short (one-line) description of the package.
Synopsis:            The best package ever!  Defines 3 so you don't have to.

-- A longer description of the package.
-- Description:         

-- URL for the project homepage or repository.
Homepage:            http://best.project.evar/

-- The license under which the package is released.
License:             BSD3

-- The file containing the license text.
License-file:        LICENSE

-- The package author(s).
Author:              Brent Yorgey

-- An email address to which users can send suggestions, bug reports,
-- and patches.
Maintainer:          byorgey@cis.upenn.edu

-- A copyright notice.
-- Copyright:           

-- Stability of the pakcage (experimental, provisional, stable...)
Stability:           Experimental

Category:            Math

Build-type:          Simple

-- Extra files to be distributed with the package, such as examples or
-- a README.
-- Extra-source-files:  

-- Constraint on the version of Cabal needed to build this package.
Cabal-version:       >=1.2


Library
  -- Modules exported by the library.
  Exposed-modules:     Foo

  -- Packages needed in order to build this package.
  -- Build-depends:       

  -- Modules not exported by this package.
  -- Other-modules:       

  -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.
  -- Build-tools:         

Notice that all the fields are commented so you know what they are for, and why you might want to edit them later. For power users who don’t want all the comments there are the --noComments and --minimal flags; consult cabal init --help for a full list of options.

Even in making this post I thought of a bunch of things that could be improved, so there’s still work to be done, but I think it’s quite usable in its current state! Give it a try and let me know of any bugs or feature requests!


Functional pearl on combinatorial species

April 7, 2010

I’ve just submitted a Functional Pearl to ICFP explaining combinatorial species in a way that is (hopefully) accessible and interesting to functional programmers. You can read the draft here — as always, comments, suggestions, etc. are welcome (although it’s too late to help my ICFP chances =).

I plan to upload a new version of my species library soon with a ton of major improvements (unlabelled enumeration, and automatically deriving species corresponding to user-defined data types via Template Haskell, to name a couple). When I do I’ll be sure to write about it here!

Also, the paper doesn’t mention it, but all the diagrams in the paper were of course generated with diagrams.


Haskell anti-pattern: incremental ad-hoc parameter abstraction

April 3, 2010

Recently I’ve found myself doing the following very ugly thing. Perhaps you’ve unwittingly done it too—so I thought I’d share the problem and its solution.

Suppose I’ve written a function foo:

> foo :: Int -> Result
> foo n = ... n ...

Who knows what Result is; that’s not the point. Everything is going fine until I suddenly realize that occasionally I would like to be able to control the number of wibbles! Well, every good programmer knows the answer to this: abstract out the number of wibbles as a parameter.

> foo :: Int -> Int -> Result
> foo numWibbles n = ... n ... numWibbles ...

But this isn’t quite what I want. For one thing, I’ve already used foo in a bunch of places in my code, and it would be annoying to go back and change them all (even with refactoring support). What’s more, most of the time I only want one wibble. So I end up doing something like this:

> foo' :: Int -> Int -> Result
> foo' numWibbles n = ... n ... numWibbles ...
> 
> foo = foo' 1

Great! Now all my old code still works, and I can use foo' whenever I want the extra control over the number of wibbles.

Well, this may seem great, but it’s a slippery slope straight to code hell. What happens when I realize that I also want to be able to specify whether the wibbles should be furbled or not? Well, I could do this:

> foo'' :: Bool -> Int -> Int -> Result
> foo'' wibblesShouldBeFurbled numWibbles n = ... 
> 
> foo' = foo'' False
> 
> foo = foo' 1

Yes, all my old code still works and I can now succesfully control the furblization if I so desire. But at what cost? First of all, this is just… well, ugly. Good luck trying to remember what foo'' does and what arguments it takes. And what if I want to furble exactly one wibble? Well, I’m stuck using foo'' True 1 because I can’t control the furblization without giving the number of wibbles explicitly.

Yes, I have actually done things like this. In fact, this problem is quite apparent in the currently released version of my diagrams library. For example:

  • hcat lays out a list of diagrams horizontally;
  • hsep is like hcat, but takes another argument specifying the amount of space to place in between each diagram;
  • hsepA is like hsep, but takes yet another argument specifying the vertical alignment of the diagrams;
  • and don’t even get me started on the distrib family of functions, which are like hcat and friends but put diagrams at evenly spaced intervals instead of putting a certain amount of space between each one…

You get the idea. So, what’s the solution? What I really want (which you may have figured out by this point) is optional, named arguments. But Haskell doesn’t have either! What to do?

I finally came up with an idea the other day… but then with a little Googling discovered that others have already thought of it. I’ve probably even read about it before, but I guess I didn’t need it back then so it didn’t stick!

Here’s the idea, as explained by Neil Mitchell: put the optional arguments to a function in a custom data structure using record syntax, and declare a record with all the default arguments. Then we can call our function using the default record, overriding whichever fields we want using record update syntax. Of course, it’s still annoying to have to remember which default record of arguments goes with which function; but the icing on the cake is that we can use a type class to provide the default arguments automatically. There’s already a suitable type class on Hackage in the data-default package.

So now my code looks something like this:

> data FooOptions = FooOptions 
>                   { wibblesShouldBeFurbled :: Bool
>                   , numWibbles :: Int
>                   }
> 
> instance Default FooOptions where
>   def = FooOptions False 1
> 
> ... foo def { wibblesShouldBeFurbled = True } ...

Nice. It might even be cool to define with as a synonym for def, to allow the natural-sounding

> ... foo with { numWibbles = 4 } ...

Of course, this isn’t perfect; if Haskell had real records it might be a bit nicer. For one thing this tends to result in a bit of namespace pollution: I can’t have another function which also takes an option called numWibbles since it will clash with the one in FooOptions! But this is still a giant improvement over the code I used to write.


Follow

Get every new post delivered to your Inbox.