String Interpolation in Haskell, or Doing Easy Things the Hard Way 5

Posted by Mark Fri, 30 Oct 2009 07:12:00 GMT

I've hacked quite a bit of Ruby and Perl in my various day jobs, and one of the things that I like about them a lot is the specialised syntax for string interpolation. It would be very hard to find a self-respecting Ruby hacker who would write
sprintf "I have %d pieces of %s at $%d apiece", number, fruit, unit_cost
or worse,
"I have "+number+" pieces of "+fruit+" at $"+unit_cost+" apiece"
when it's possible to write
"I have #{number} pieces of #{fruit} at $#{unit_cost} apiece"
This is the sort of syntactic polish that Ruby excels at, and it's an area where Haskell lags a little: it's not a terribly sexy feature, semantically speaking, and for structured data it's inappropriate anyway - you should be defining a Show instance instead, or possibly Binary if it's large. For the cases it's good for, though, it's very good, and I miss it in Haskell.

Now, this feature is trivial to implement in a dynamically typed language. If it weren't already there, we could easily write code to parse out the #{ } references, and call "eval" to get their values in the current scope - after that, it's just a case of pasting it together. Statically typed languages, however, generally don't have eval, and while Template Haskell does allow you to do some static computation, it stops short of full 'eval' functionality.

I poked at the problem for a while and was about ready to give in when Matt Morrow (mmorrow on #freenode) posted a beautiful solution. Before I go into the solution, here's the interface:

fruit = "apple"
unit_cost = 2
number = 10
result=[$here|I have $(number) pieces of $(fruit) at $(unit_cost) dollars apiece, making $(unit_cost * number) dollars|]

Our syntax here is a little different because we're using Template Haskell, but we've kept all of the important parts: our references are still inline, and we can use expressions, not just variables.

Now, for the implementation: Matt's actually written a parser that can take an arbitrary string to the Template Haskell expression equivalent. The quasiquoter defined as 'here' takes the text inside the $( ) brackets, tries to build a Haskell expression out of it, and inserts it as a string into the result, concatenating them all together. The nicest bit is that this is completely typesafe, so we have no nasty surprises at runtime.

the code is here, but it's probably easier to grab it with

sudo cabal install haskell-src-meta

to use it, you'll need

{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
import Language.Haskell.Meta.QQ.HsHere

at the top of your file, and to include the package 'haskell-src-meta' in your cabal file (or on the ghci command line).

random list of things i found over the weekend

Posted by Mark Tue, 06 Oct 2009 05:20:00 GMT

  1. Flymake mode for Emacs and Haskell is very cool, but needs a bit of poking to get right for literate haskell files
  2. Some of my Haskell from six months ago is … not very good. Every time I see a fromJust, I cringe a little inside

mac dylibs in GHC HEAD

Posted by Mark Thu, 01 Oct 2009 03:06:00 GMT

to my very great delight, Stephen Blackheath has taken my scrappy little patches and put in the hours to get dylibs working properly on Mac, all the way through. They're not in the main line yet, but they should be soon - if you'd like to play, they're listed on the bug tracker here and here. These make installing and running cabal packages on Mac pretty smooth, so I'll be splitting off the Haskell in Hubris into its own Cabal package and making a release as soon as I've ironed out a couple of bugs.