liboleg-0.2: Text/TFTest.hs
{-# LANGUAGE TemplateHaskell, NoMonomorphismRestriction #-}
-- The rest is Haskell98
-- | Type-safe printf and scanf with C-like formatting string
-- We also permit a generic format specifier %a to format or parse
-- any showable and readable value.
-- Our format descriptors are first-class and can be built incrementally.
-- We can use the same format descriptor to format to a string,
-- to the standard output or any other data sink. Furthermore,
-- we can use the same format descriptor to parse from a string
-- or any data source.
-- What we print we can parse, using the same descriptor.
module Text.TFTest where
import Text.TotalPrintF
import Prelude hiding ((^))
-- The following import is needed only for the extensibility test
-- at the end of the file
import Text.PrintScanF (FormattingSpec(..), PrinterParser(..))
t1 = sprintf $(spec "Hello, there!")
-- "Hello, there!"
t1s = sscanf "Hello, there!" $(spec "Hello, there!") ()
-- Just ()
t2 = sprintf $(spec "Hello, %s!") "there"
-- "Hello, there!"
t3 = sprintf $(spec "The value of %s is %d") "x" 3
-- "The value of x is 3"
-- What we print we can parse, using the same descriptor
t31 = let printed = sprintf desc "x" 3
parsed = sscanf printed desc (\s i -> (s,i))
in (printed, parsed)
where desc = $(spec "The value of %s is %d")
-- ("The value of x is 3",Just ("x",3))
-- Mismatch between the formatting string and the printf arguments
-- is a type error.
-- t32 = sprintf $(spec "The value of %s is %d") "x" True
-- Couldn't match expected type `Bool' against inferred type `Int'
{-
t33 = sprintf $(spec "The value of %s is %d") "x" 3 10
Couldn't match expected type `t1 -> t'
against inferred type `String'
Probable cause: `printf' is applied to too many arguments
-}
t4 = let x = [9,16,25]
i = 2
in sprintf $(spec "The element number %d of %a is %a") i x (x !! i)
-- "The element number 2 of [9,16,25] is 25"
t4s = sscanf "The element number 2 of [9,16,25] is 25"
$(spec "The element number %d of %a is %a")
(\i a e -> (i,a::[Int],e::Int))
-- Just (2,[9,16,25],25)
-- Demonstrating that printf is first-class
t5 = map (uncurry printer) [("x",3), ("y",5), ("z",7)]
where
printer = sprintf $(spec "The value of %s is %d")
-- ["The value of x is 3","The value of y is 5","The value of z is 7"]
{- An inconsistent format descriptor generates a type error
t6 = map (uncurry printer) [("x",3), ("y",5), ("z",7)]
where
printer = sprintf $(spec "The value of %d is %d")
Couldn't match expected type `Int' against inferred type `[Char]'
In the expression: "x"
In the expression: ("x", 3)
In the second argument of `map', namely
`[("x", 3), ("y", 5), ("z", 7)]'
-}
-- Demonstrating that format descriptors are first-class and can be built
-- incrementally
spec71 = $(spec "The value of %s")
spec72 = $(spec " is %d")
t7 = sprintf (spec71 ^ spec72) "x" 3
-- "The value of x is 3"
-- Extensibility test
-- Furthermore, the user can always extend printf, for example, to
-- send the formatted data to the standard output, without first building
-- the formatted output as a string.
newtype FPrIO a b = FPrIO ((IO () -> a) -> b)
instance FormattingSpec FPrIO where
lit str = FPrIO $ \k -> k $ putStr str
int = FPrIO $ \k -> \x -> k $ putStr (show x)
char = FPrIO $ \k -> \x -> k $ putStr [x]
fpp (PrinterParser pr _) = FPrIO $ \k -> \x -> k $ putStr (pr x)
(FPrIO a) ^ (FPrIO b) = FPrIO $ \k -> a (\sa -> b (\sb -> k (sa >> sb)))
printf (FPrIO fmt) = fmt (\m -> m >> putStrLn "\n-end-of-output-")
t3io = printf $(spec "The value of %s is %d") "x" 3
{- printed on the standard output:
The value of x is 3
-end-of-output-
-}