#Format Source

data Format monoid result f

A String formatter, like printf, but type-safe and composable.


import Text.Formatting (print, s, string)

Build up a Format, composing with <<<.

greeting :: forall r. Format String r (String -> r)
greeting = s "Hello " <<< string <<< s "!"

Convert it to a function with print:

greet :: String -> String
greet = print greeting

Then use it:

message1 :: String
message1 = greet "Kris"
--> message1 == "Hello Kris!"

Or more often, use it directly:

message2 :: String
message2 = print greeting "Kris"
--> message2 == "Hello Kris!"

What really sets this approach apart from string interpolation, apart from the type-safety, is that we can freely compose Formats. Let's extend greeting with some more arguments:

inbox :: forall r. Format String r (String -> Int -> r)
inbox = greeting <<< s " You have " <<< <<< s " new messages."

print still makes it into a function:

welcome :: String -> Int -> String
welcome = print inbox

Or again, call it in one go:

message3 :: String
message3 = print inbox "Kris" 3
--> message3 == "Hello Kris! You have 3 new messages."

A Guide To The Types

As an example, a function that behaves like printf "%s: %d" would will have the type signature Format String r (String -> Int -> r). This tells you that:

  • Format String - This is a Format that will eventually yield a String.
  • r - This keeps the final type of the formatter open.
  • (String -> Int -> r) - The formatter takes a String, then an Int, and is open to further extension.


  • Format ((monoid -> result) -> f)


  • Semigroupoid (Format String)

    Note to interested readers: Format should be a Semigroupoid - and hence composable with <<< for any format of type forall m r f. Semigroupoid m => Format m r f

    However, I don't know how to persuade PureScript of that. Or even if it's valid to say, "This is a member of that category, provided you meet my extra constraints..."

    Nevertheless, for the most common format - the one that yields Strings, it's composable. And that probably all most people will care about.

#composeFormat Source

composeFormat :: forall f m s r. Semigroup m => Format m r f -> Format m s r -> Format m s f

#print Source

print :: forall r f. Format r r f -> f

Turns a Format into the underlying function it has built up. Call this when you're ready to apply all the arguments and generate an r (usually a String).

#apply Source

apply :: forall b a m r. Format m r (a -> b) -> a -> Format m r b

Apply the first argument of the formatter, without unwrapping it to a plain ol' function.

#toFormatter Source

toFormatter :: forall a m r. (a -> m) -> Format m r (a -> r)

Turn a function into a Format.

#before Source

before :: forall f b a m r. (b -> a) -> Format m r (a -> f) -> Format m r (b -> f)

Modify a Format so that this (contravariant) function is called on its first argument.


import Text.Formatting as F
print (F.before length [1, 2, 3]
--> "3"

#after Source

after :: forall f n m r. (m -> n) -> Format m r f -> Format n r f

Modify a Format so that this function is called on its final result.


import Text.Formatting as F
print (F.after toUpper show) (Just 3)
--> "(JUST 3)"

#show Source

show :: forall a r. Show a => Format String r (a -> r)

Accept any Showable argument.

#string Source

string :: forall r. Format String r (String -> r)

Accept a String.

#int Source

int :: forall r. Format String r (Int -> r)

Accept an Int.

#number Source

number :: forall r. Format String r (Number -> r)

Accept a Number.

#boolean Source

boolean :: forall r. Format String r (Boolean -> r)

Accept a Boolean.

#s Source

s :: forall r. String -> Format String r r

Insert a fixed string.