MissingH API ManualContentsIndex
MissingH.Printf
Portabilityportable
Stabilityprovisional
MaintainerJohn Goerzen <jgoerzen@complete.org>
Contents
Introduction
Methods of Use
Variable-Argument Ouptut
Casting Notes
List-Argument Output
Mapping Output Types
Association List Output
FiniteMap Output
Generic/Custom Output
Utility Function
Differences from C
Important Haskell Notes
Full Example Programs
Underlying Types
Description

This module provides various helpful utilities for using a C-style printf().

Copyright (c) 2004 John Goerzen, jgoerzen@complete.org

Some code in sub-modules written by Ian Lynagh

Inspiration and ideas from haskell-xml-rpc by Bjorn Bringert

Please scroll down to read the detailed documentation.

NOTE: to use this module under ghc, you must use:

 -fallow-overlapping-instances

With hugs:

 -98 +o
Synopsis
vsprintf :: PFRun a => String -> a
vprintf :: IOPFRun a => String -> a
vfprintf :: IOPFRun a => Handle -> String -> a
ps :: String -> String
pio :: IO () -> IO ()
sprintf :: String -> [Value] -> String
printf :: String -> [Value] -> IO ()
fprintf :: Handle -> String -> [Value] -> IO ()
sprintfAL :: String -> [(String, Value)] -> String
printfAL :: String -> [(String, Value)] -> IO ()
fprintfAL :: Handle -> String -> [(String, Value)] -> IO ()
sprintfFM :: String -> FiniteMap String Value -> String
printfFM :: String -> FiniteMap String Value -> IO ()
fprintfFM :: Handle -> String -> FiniteMap String Value -> IO ()
sprintfG :: (String -> a -> Maybe Value) -> String -> a -> String
printfG :: (String -> a -> Maybe Value) -> String -> a -> IO ()
fprintfG :: Handle -> (String -> a -> Maybe Value) -> String -> a -> IO ()
v :: PFType a => a -> Value
data Value
= ValueRational Rational
| ValueString String
| ValueChar Char
class PFType a where
toValue :: a -> Value
fromValue :: Value -> a
Introduction

Welcome to the Haskell printf support. This module is designed to emulate the C printf(3) family of functions. Here are some quick introductory examples:

vsprintf "Hello"
> "Hello"
vsprintf "Hello, %s\n" "John"
> "Hello, John\n"
vsprintf "%s, your age is %d\n" "John" (10::Integer)
> "John, your age is 10\n"

Or, using the list-passing method:

sprintf "Hello" []
> "Hello"
sprintf "Hello, %s\n" [v "John"]
> "Hello, John\n"
sprintf "%s, your age is %d\n" [v "John", v (10::Integer)]
> "John, your age is 10\n"

Or, using the association list method:

sprintfAL "%(name)s, your age is %(age)d\n"
  [("name", v "John"),
   ("age", v (10::Integer))]
> "John, your age is 10\n"

You can also work with I/O with these:

main :: IO ()
main = do
       pio $ vprintf "Line1\n"
       pio $ vprintf "Line2: %s\n" "blah"
       vprintf "Line3: done\n"

This will print Line1\nLine2: blah\nLine3: done\n to standard output. You can also use the list form:

main :: IO ()
main = do
       printf "Line1\n" []
       printf "Line2: %s\n" [v "blah"]
       printf "Line3: done\n" []
Methods of Use
As you can see, there are two different ways to access the printf functions: via the variable argument count support (the functions beginning with v) or via the list argument support. There is a utility function, v, that is simply a shortcut for toValue.
Variable-Argument Ouptut
vsprintf :: PFRun a => String -> a
Given a format string and zero or more arguments, return a string that has formatted them appropriately. This is the variable argument version of sprintf.
vprintf :: IOPFRun a => String -> a
Like vfprintf, but directs output to standard out instead of taking an explicit Handle.
vfprintf :: IOPFRun a => Handle -> String -> a
Like vsprintf, but instead of returning a string, directs output to the given Handle.
Casting Notes

If you are running in an interactive situation, or something where the compiler cannot deduce the expected return type, you will need to cast it to String. For instance, at the ghci prompt, you would have to say (sprintf "foo")::String to make things work. If you are using one of the I/O variants, you will have to instead cast it to IO ().

To make this easier, there are two functions: ps and pio. They simply provide an easy idiom to force things to the proper type. Examples:

main :: IO ()
main = do
       pio $ vprintf "Line1\n"
       pio $ vprintf "Line2: %s\n" "blah"
       vprintf "Line3: done\n"

Note that in this case, no pio was necessary for the third line. That's because main was declared to return IO () already, so the type system knows what to do. If that declaration was missing, the pio would have been required there as well.

These special cases apply only to the "v" functions.

ps :: String -> String
Utility to force something to a string
pio :: IO () -> IO ()
Utility to force something to an IO ()
List-Argument Output
sprintf :: String -> [Value] -> String
List version of vsprintf.
printf :: String -> [Value] -> IO ()
Like fprintf, but directs output to standard out instead of taking an explicit Handle.
fprintf :: Handle -> String -> [Value] -> IO ()
Like sprintf, but instead of returning a string, directs output to the given Handle.
Mapping Output Types

As a special extension to the printf() format string syntax, special functions can take a key name in the format string. This key will then be looked up in an association list or FiniteMap passed in. Python programmers will find this very similar to Python's % operator, which can look up inside dicts.

Here's an example:

import MissingH.Printf

al = [("item1", v "Test One"),
      ("blah", v (5::Int)),
      ("zip", v (3.14::Float))]

main :: IO ()
main = do
       printfAL "%(item1)s: %(blah)03d, %(zip)06.3f; %(item1)s\n" al

This will print:

Test One: 005, 03.140; Test One
Association List Output
sprintfAL :: String -> [(String, Value)] -> String
Association list version of sprintf.
printfAL :: String -> [(String, Value)] -> IO ()
Like fprintfAL, but directs output to standard out instead of taking an explicit Handle.
fprintfAL :: Handle -> String -> [(String, Value)] -> IO ()
Like sprintfAL, but instead of returning a string, directs output to the given Handle.
FiniteMap Output
sprintfFM :: String -> FiniteMap String Value -> String
Finite map version of sprintf.
printfFM :: String -> FiniteMap String Value -> IO ()
Like fprintfFM, but directs output to standard out instead of taking an explicit Handle.
fprintfFM :: Handle -> String -> FiniteMap String Value -> IO ()
Like sprintfFM, but instead of returning a string, directs output to the given Handle.
Generic/Custom Output
sprintfG
:: (String -> a -> Maybe Value)Lookup function
-> StringFormat string
-> aLookup data
-> StringReturn value

Generic version of sprintf.

This is one of the map-style functions and provides a way for you to integrate your own lookup support into sprintf. The first parameter is a lookup function. It takes a variable name and a map data object and returns a sought-after Value. It is expected to return Nothing if the given key could not be found.

You might be interested to know that related helper functions can be defined thusly:

sprintfAL = sprintfG lookup
sprintfFM = sprintfG (flip lookupFM)
printfG :: (String -> a -> Maybe Value) -> String -> a -> IO ()
Like fprintfG, but directs output to standard out instead of taking an explicit Handle.
fprintfG :: Handle -> (String -> a -> Maybe Value) -> String -> a -> IO ()
Like sprintfG, but instead of returning a string, directs output to the given Handle.
Utility Function
v :: PFType a => a -> Value
Differences from C

These functions are very similar to the C functions. Here is a list of the known differences:

  • There is a new conversion type %H. This will take any type of data already converted to a value and display it in its native representation from show. This may not be what you expect for integers, and is likely to be altered in the future, so use with caution.
  • There is no support for the length specifiers (l, ll, etc.) since Haskell's type system provides all the information we need.
  • There is no support for the %n, %*, %$ forms that the C printf() supports. These make less sense in Haskell.
Important Haskell Notes

Please be aware of the following as you use this module:

If the type system cannot determine the type of an argument (as in the numeric literals in the examples in the introduction), you may have to explicitly cast it to something. In practice, this is only a problem in interactive situations like ghci or hugs.

Floating-point values are converted to a Double for display. If you are using some floating-point value with an extremely high precision (such as a Rational), be aware that some of this precision may be lost for display.x

When run with Hugs, you must use -98 +o on your command line.

Full Example Programs

Here are some full example programs. You can compile and run these directly.

This example acts as a filter that adds a line number and length to each line from input:

import MissingH.Printf

convlines :: Int -> [String] -> [String]
convlines _ [] = []
convlines count (line:xs) =
    vsprintf "%6d, len %03d: %s" count (length line) line : 
            convlines (count + 1) xs

main = interact $ unlines . convlines 1 . lines

If you have a sample file like this:

Hello,

This is a test.
Haskell is really neat.

Then running ./test < file.txt will produce:

     1, len 006: Hello,
     2, len 000:
     3, len 015: This is a test.
     4, len 023: Haskell is really neat.

And so on -- and everything will be nicely lined up since the line numbers will grow to the left.

Here's another example of a little bit of interaction:

import MissingH.Printf
import System.IO

main = do
       hSetBuffering stdout NoBuffering
       printf "Welcome.  Please enter your name: " []
       name <- getLine
       printf "Hello, %s.  Please enter your age: " [v name]
       agestr <- getLine
       let age = (read agestr)::Int
       printf "%s, you are at least %d months old.\n" [v name, v $ age * 12]

Here's a sample session:

Welcome.  Please enter your name: Bill
Hello, Bill.  Please enter your age: 53
Bill, you are at least 636 months old.

The printf functions are also great for creating reports nicely lined up by column. Here's an example:

import MissingH.Printf
import MissingH.IO
import Data.List

fmt = "%-10d %010d %010X"

fmtlines :: Int -> [String] -> [String]
fmtlines _ [] = []
fmtlines count (x:xs) =
    let l = length x in
        vsprintf fmt count l l : fmtlines (count + 1) xs

main = do
       pio $ vprintf ("%-10s %-10s %s\n") "Line #" "Length Dec" "Length Hex"
       putStrLn $ (replicate 10 '-') ++ " " ++ (replicate 10 '-') ++
                " " ++ (replicate 10 '-')
       lineInteract $ fmtlines 1

When applied to the same example file as before, the output will be:

Line #     Length Dec Length Hex
---------- ---------- ----------
1          0000000006 0000000006
2          0000000000 0000000000
3          0000000015 000000000F
4          0000000023 0000000017

There's a full association list example elsewhere in this document.

Underlying Types
data Value
All items to be printed must be expressible as one of these.
Constructors
ValueRational Rational
ValueString String
ValueChar Char
show/hide Instances
Eq Value
Ord Value
Show Value
class PFType a where
The class to which all items must belong (unless you want to inconvenience everyone and force them to manually generate Values.
Methods
toValue :: a -> Value
fromValue :: Value -> a
show/hide Instances
PFType Char
PFType Double
PFType Integer
PFType String
Real a => PFType a
Produced by Haddock version 0.7