31. IsString Abuse

The IsString type-class and the OverloadedStrings extension were meant to save Haskell programmers from having to type {ByteString,Text}.pack over and over again, but they can be used in more creative ways as well.

I like to use printf to format my strings, but I always forget to type the actual printf call, and just type the format string followed by the arguments.

With OverloadedStrings, we can treat the format string as a call to printf. Here’s how:

{-# LANGUAGE OverloadedStrings, FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

import Data.String ( IsString(..) )
import Text.Printf ( PrintfType, printf )

instance (PrintfType a) => IsString a where
    fromString s = printf s

main :: IO ()
main = do
    let x = 23 :: Int
        s = "World!" :: String
    "%d. Hello, %s\n" x s

The bit that turns string literals into calls to printf is the IsString instance for PrintfType. The last line shows the process in action.

The downside is that the types of the arguments need to be fixed for this to work, which makes short examples programs look weird, but shouldn’t be a problem for larger programs where the types are fixed anyway.

Another problem is that, like with printf, we only handle a few primitive types. We can get around this by writing our own printf which works for any types with Show instances.