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?
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.
ReplyDeleteOh and Racket does come with a lazy dialect, and perhaps one day with typed/lazy.
-- Matthias
Liskell?
ReplyDeletehttp://www.liskell.org/fifteen-minute-tour