Safe Haskell | Safe |
---|---|

Language | Haskell2010 |

- module Control.Category
- type Format m = Cokleisli ((->) m)
- printfWith :: (m -> r) -> Format m r b -> b
- sprintf :: Format m m b -> b
- c :: Monoid m => m -> Format m a a
- i :: Format m a (m -> a)
- spliceWith :: Monoid m => (t -> m) -> Format m a (t -> a)
- s :: (Monoid s, IsString s, Show t) => Format s a (t -> a)
- bind :: (t -> Format s a b) -> Format s a (t -> b)
- mapMonoid :: (m -> m') -> Format m a b -> Format m' a b
- generalizeString :: (IsString s, Monoid s) => Format String a b -> Format s a b
- signedWith :: (Num t, Ord t, Monoid s) => (s -> s) -> (s -> s) -> Format s a (t -> b) -> Format s a (t -> b)
- intAtBase :: (Real t, Integral t, Show t, Monoid s, IsString s) => t -> (Int -> Char) -> Format s a (t -> a)
- hex :: (Integral t, Show t, Monoid s, IsString s) => Format s a (t -> a)
- oct :: (Integral t, Show t, Monoid s, IsString s) => Format s a (t -> a)
- eFloat :: (RealFloat t, Monoid s, IsString s) => Maybe Int -> Format s a (t -> a)
- fFloat :: (RealFloat t, Monoid s, IsString s) => Maybe Int -> Format s a (t -> a)
- gFloat :: (RealFloat t, Monoid s, IsString s) => Maybe Int -> Format s a (t -> a)
- push :: Monoid m => t -> Format m (t -> a) a
- dup :: Monoid m => Format m (t -> t -> a) (t -> a)
- swap :: Monoid m => Format m (t -> t' -> a) (t' -> t -> a)
- skip :: Monoid m => Format m a (t -> a)
- apply :: Monoid m => (u -> v) -> Format m (v -> a) (u -> a)
- apply2 :: Monoid m => (u -> v -> w) -> Format m (w -> a) (u -> v -> a)

# Documentation

Note that you'll probably want to import both this module and one of the other string-type specific modules depending on which sort of strings you'll be working with. You'll also probably want to turn on the OverloadedStrings extension so as to be able to use string literals as formatters which insert themselves.

module Control.Category

# Basics

type Format m = Cokleisli ((->) m) Source

Handy type synonym for the things we're working with.
You should regard a value of type `Format m a b`

as something which explains how to write
some element of the monoid `m`

(a "string" for our purposes), and which will change the type
of printf from `a`

to `b`

. For instance, something which adds a responsibility to provide an
additional argument of type `t`

might have type `Format m a (t -> a)`

, while a formatter which
somehow absolves you of that responsibility would have type `Format m (t -> a) a`

.

printfWith :: (m -> r) -> Format m r b -> b Source

We can apply this to something like putStrLn to get a function for formatted printing.
Typically you'll have `r = IO ()`

, but that needn't be the case.

sprintf :: Format m m b -> b Source

If you just want to build a string / element of your monoid, we have `sprintf = printfWith id`

c :: Monoid m => m -> Format m a a Source

Formatter for a constant string. Note that for string literals, this is implicit if you turn on the OverloadedStrings extension.

`>>>`

Hello, world!`let x = "world" in printfLn (c "Hello, " . c x . c "!")`

i :: Format m a (m -> a) Source

Inclusion of a string as an argument.

`>>>`

Hello, Anne! Hello, Bob! Hello, world!`mapM_ (printfLn ("Hello, " . i . "!")) ["Anne", "Bob", "world"]`

spliceWith :: Monoid m => (t -> m) -> Format m a (t -> a) Source

Given a way to turn a value of type t into a string, this builds a formatter which demands an additional argument of type t and splices it in.

s :: (Monoid s, IsString s, Show t) => Format s a (t -> a) Source

Splice in anything showable.

`>>>`

list: [1,2,3] tuple: ("hello",'a') string: "there"`printfLn ("list: " . s . " tuple: " . s . " string: " . s) [1,2,3] ("hello", 'a') "there"`

bind :: (t -> Format s a b) -> Format s a (t -> b) Source

Select which formatter to apply based on an argument.

`>>>`

5328.0`printfLn (bind (\b -> if b then eFloat Nothing else fFloat Nothing)) False 5328`

`>>>`

5.328e3`printfLn (bind (\b -> if b then eFloat Nothing else fFloat Nothing)) True 5328`

`>>>`

5.328e3`printfLn (bind (\f -> f Nothing)) eFloat 5328`

mapMonoid :: (m -> m') -> Format m a b -> Format m' a b Source

Apply a function to the output of a formatter, possibly changing its type.

`>>>`

CAFE1234`printfLn (mapMonoid (map toUpper) hex) 0xcafe1234`

generalizeString :: (IsString s, Monoid s) => Format String a b -> Format s a b Source

Generalizes the string type that a formatter uses by applying fromString internally.

# Numeric formatting

signedWith :: (Num t, Ord t, Monoid s) => (s -> s) -> (s -> s) -> Format s a (t -> b) -> Format s a (t -> b) Source

Transform a numeric formatter which will be used to handle absolute values, applying the first given function to the string to be spliced when the argument is negative, and the second given function otherwise.

`>>>`

(3.14)`printfLn (signedWith (\v -> mconcat ["(",v,")"]) id (fFloat (Just 2))) (-pi)`

`>>>`

-00439`printfLn (signedWith ("-"<>) ("+"<>) (padLeft "0" 5 s)) (-439)`

`>>>`

+01278`printfLn (signedWith ("-"<>) ("+"<>) (padLeft "0" 5 s)) 1278`

intAtBase :: (Real t, Integral t, Show t, Monoid s, IsString s) => t -> (Int -> Char) -> Format s a (t -> a) Source

Show an integral value using the given base, and using the provided function to determine how to display individual digits.

hex :: (Integral t, Show t, Monoid s, IsString s) => Format s a (t -> a) Source

Show an integral value in hexadecimal.

`>>>`

51966 in hexadecimal is cafe`printfLn (dup . s . " in hexadecimal is " . hex) 51966`

oct :: (Integral t, Show t, Monoid s, IsString s) => Format s a (t -> a) Source

Show an integral value in octal.

eFloat :: (RealFloat t, Monoid s, IsString s) => Maybe Int -> Format s a (t -> a) Source

Show a floating point value in exponential format. (e.g. 2.45e2, -1.5e-3)
If `digs`

is Nothing, the value is shown to full precision, if it is Just d then at most
d digits after the decimal point are shown.

fFloat :: (RealFloat t, Monoid s, IsString s) => Maybe Int -> Format s a (t -> a) Source

Show a floating point value in standard decimal format. (e.g. 245000, -0.0015)
If `digs`

is Nothing, the value is shown to full precision, if it is Just d then at most
d digits after the decimal point are shown.

gFloat :: (RealFloat t, Monoid s, IsString s) => Maybe Int -> Format s a (t -> a) Source

Show a floating point value using standard decimal notation for arguments whose absolute
value lies between 0.1 and 9,999,999, and scientific notation otherwise.
If `digs`

is Nothing, the value is shown to full precision, if it is Just d then at most
d digits after the decimal point are shown.

# Argument stack manipulation

push :: Monoid m => t -> Format m (t -> a) a Source

We can use `arr`

from the Arrow instance for Cokleisli w to produce formatters
that manipulate the stack without printing. That is, we have

arr :: (Monoid m) => (s -> s') -> Format m s s'

Push an argument onto the stack to be consumed by subsequent formatters.

dup :: Monoid m => Format m (t -> t -> a) (t -> a) Source

Duplicate an argument on the stack, making it available twice.

swap :: Monoid m => Format m (t -> t' -> a) (t' -> t -> a) Source

Swap the next two arguments on the stack.

apply :: Monoid m => (u -> v) -> Format m (v -> a) (u -> a) Source

Apply a function to the argument on the top of the stack.

apply2 :: Monoid m => (u -> v -> w) -> Format m (w -> a) (u -> v -> a) Source

Apply a binary function to the top two arguments on the stack.

`>>>`

4 plus 6 equals 10`printfLn (dup . s . " plus " . swap . dup . s . " equals " . apply2 (+) . s) 4 6`

`>>>`

4 plus 6 equals 10`printfLn (arr (\k x y -> k x y (x+y)) . s . " plus " . s . " equals " . s) 4 6`