Wednesday, October 6, 2010

Verbose show and the missing preprocessor

Verbose show

Say we want a function vshow which describes an expression before showing its value. For example:

vshow (2 + 3)          = "2 + 3 = 5"
vshow (product [1..5]) = "product [1..5] = 120"

Clearly vshow can't be an ordinary Haskell function. We just don't have that kind of runtime access to program source.

All sorts of workarounds come to mind: simple-reflect, feeding strings to hint, Template Haskell… But it's hard to beat the simplicity of the humble C preprocessor:

{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -pgmP cpp #-}

#define vshow(e) (#e ++ " = " ++ show (e))

main :: IO ()
main = do
putStrLn vshow(2 + 3)
putStrLn vshow(product [1..5])

GHC's built-in CPP can't handle the stringify operator, so we invoke the "real" C preprocessor with -pgmP cpp. Testing it:

$ runhaskell vshow.hs
2 + 3 = 5
product [1..5] = 120

Like the other solutions I mentioned, this has its drawbacks. vshow isn't a first-class function, and we have to use C rather than Haskell function-call syntax. It's not terribly flexible, but it is one line of reasonably clear code, and I can think of a few small use cases.

The missing preprocessor

Besides sharing a cute little trick, I wanted to start a conversation about text-level preprocessors for Haskell. CPP is used throughout the standard library for conditional compilation, boilerplate class instances, etc. At the same time, it's quite limited, and not a good syntactic fit for Haskell.

Template Haskell is at the opposite end of the power/complexity spectrum. It might be what you want for programmatically generating complicated abstract syntax trees. It's not a good fit for simply "copy-pasting" some text into a few locations. For those tasks we shy away from the enormous complexity of TH. We use CPP or, well, copy-paste. The latter is not acceptable for a language that's advertising such a high level of abstraction. We may write less boilerplate than in Java or C++, but it still gets on the nerves.

I think there's an unfilled niche for lightweight text-level preprocessing, with just a bit more flexibility than CPP. GHC makes it easy to plug in any preprocessor you like, using -F -pgmF. We can start experimenting today.

m4 is a powerful and venerable preprocessor. It comes with most UNIX systems and has an open-source implementation. Yet I've not seen any Haskell project use it for Haskell source. Has anyone used m4 in this role?

I found an old paper (.ps.gz) with some proposals. Anyone have comments?

There's a number of preprocessors for implementing specific language features, including arrows, indexed monads, C bindings, and Conor McBride's intriguing Strathclyde Haskell Enhancement. The package preprocessor-tools seems to be a framework for writing this sort of preprocessor.

So what have you used for preprocessing? What features would you like to see? What design philosophy?

2 comments:

  1. Take a look at Racket's 'macro' system. It's an API for the front-end and you'll find it helps you with a lot more than the simple example above.

    Oh and Racket does come with a lazy dialect, and perhaps one day with typed/lazy.

    -- Matthias

    ReplyDelete
  2. Liskell?

    http://www.liskell.org/fifteen-minute-tour

    ReplyDelete