A type-safe string formatting library. It fulfils the need for
string-interpolation or a printf
function, without sacrificing Elm's
runtime guarantees or requiring any language-level changes. It also
composes well, to make building up complex formatters easy.
A formatter. This type holds all the information we need to create a formatting function, wrapped up in a way that makes it easy to compose.
Build one of these up with primitives like s
, string
and int
,
join them together with |> bs
, and when you're done, generate the final
printing function with print
.
import Formatting exposing (..)
greeting =
s "Hello " |> bs string |> bs "!"
print greeting "Kris"
--> "Hello Kris!"
Imagine you have an existing formatting rule you'd like to turn into a formatter:
tweetSummary : Int -> String -> String
tweetSummary starCount body =
"(" ++ toString starCount ++ ") " ++ body
First, wrap the type signature in brackets:
tweetSummary : Int -> String -> String
Then change the result type to a variable. (That's where the magic begins - the Formatting library gets control of the final result type.):
tweetSummary : Int -> String -> r
Now add Format r
to the start.
tweetSummary : Format r (Int -> String -> r)
All very mechanical. Now for the function body. Let's recall what it looked like at the start:
tweetSummary starCount body =
"(" ++ toString starCount ++ ") " ++ body
Change that into an anonymous function:
tweetSummary =
\starCount body ->
"(" ++ toString starCount ++ ") " ++ body
Now add in a callback
function as the first argument:
tweetSummary =
\callback starCount body ->
"(" ++ toString starCount ++ ") " ++ body
Pass your function's result to that callback (using <|
is the easy way):
tweetSummary =
\callback starCount body ->
callback <| "(" ++ toString starCount ++ ") " ++ body
Finally, wrap that all up in a Format
constructor:
tweetSummary =
Format
(\callback starCount body ->
callback <| "(" ++ toString starCount ++ ") " ++ body
)
And you're done. You have a composable formatting function. It's a mechanical process that's probably a bit weird at first, but easy to get used to.
Format (\callback -> g (\strG -> f (\strF -> callback (strG ++ strF))))
bs : Format b a -> Format a c -> Format b c
Compose two formatters together.
map : (String -> String) -> Format r a -> Format r a
Create a new formatter by applying a function to the output of this formatter.
For example:
import String exposing (toUpper)
format =
s "Name: " |> bs map toUpper string
...produces a formatter that uppercases the name:
print format "Kris"
--> "Name: KRIS"
premap : (a -> b) -> Format r (b -> v) -> Format r (a -> v)
Create a new formatter by applying a function to the input of this
formatter. The dual of map
.
For example:
format =
s "Height: " |> bs premap .height float
...produces a formatter that accesses a .height
record field:
print format { height: 1.72 }
--> "Height: 1.72"
toFormatter : (a -> String) -> Format r (a -> r)
Convert an ordinary 'stringifying' function into a Formatter.
apply : Format r (a -> b -> r) -> a -> Format r (b -> r)
Apply an argument to a Formatter. Useful when you want to supply
an argument, but don't yet want to convert your formatter to a plain
ol' function (with print
).
print : Format String a -> a
Turn your formatter into a function that's just waiting for its arguments.
Given this format:
orderFormat =
s "FREE: " |> bs int |> bs s " x " |> bs string |> bs s "!"
...we can either use it immediately:
order : String
order =
print orderFormat 2 "Ice Cream"
--> "FREE: 2 x Ice Cream!"
...or turn it into an ordinary function to be used later:
orderFormatter : Int -> String -> String
orderFormatter =
print orderFormat
...elsewhere...
order : String
order = orderFormatter 2 "Ice Cream"
--> "FREE: 2 x Ice Cream!"
html : Format (Html msg) a -> a
Convenience function. Like print
, but returns an Html.text
node as its final result, instead of a String
.
Hint: If you're using any formatters where whitespace is sigificant, you might well need one or both of these CSS rules:
font-family: monospace;
white-space: pre;
s : String -> Format r r
A boilerplate string.
string : Format r (String -> r)
A placeholder for a String
argument.
int : Format r (Basics.Int -> r)
A placeholder for an Int
argument.
bool : Format r (Basics.Bool -> r)
A placeholder for an Bool
argument.
float : Format r (Basics.Float -> r)
A placeholder for a Float
argument.
wrap : String -> Format r a -> Format r a
wrap
one string with another. It's convenient for building strings
like `"Invalid key '
print (wrap "'" string) "tester"
--> "'tester'"
pad : Basics.Int -> Char -> Format r a -> Format r a
String.pad
lifted into the world of Formatters.
For example:
print (pad 10 '-' string) "KRIS"
--> "---KRIS---"
padLeft : Basics.Int -> Char -> Format r a -> Format r a
String.padLeft
lifted into the world of Formatters.
For example:
print (padLeft 10 '_' float) 1.72
--> "______1.72"
padRight : Basics.Int -> Char -> Format r a -> Format r a
String.padRight
lifted into the world of Formatters.
For example:
print (padRight 10 '.' int) 789
--> "789......."
dp : Basics.Int -> Format r (Basics.Float -> r)
DEPRECATED: Use roundTo
instead.
roundTo : Basics.Int -> Format r (Basics.Float -> r)
A float rounded to n
decimal places.
uriFragment : Format r (String -> r)
Format a URI fragment.
For example:
print uriFragment "this string"
--> "this%20string"