The FormatComb structure
Author: John Reppy
Keywords: Standard ML, SML, SML/NJ Library

The SML of NJ Library
The `FormatComb` structure

The `FormatComb` structure provides well-typed formating (or unparsing) combinators in the style of Olivier Danvy’s Functional Unparsing work.

The idea is to use combinators for constructing something akin to the format string of C's `printf` function. The difference is, however, that our formats are not strings. Instead, format fragments have meaningful types, and passing them to the `format` function results in a curried function whose arguments have precisely the types that correspond to argument-consuming parts of the format. (Such argument-consuming parts are similar to the conversion-specifications the `Format` structure.)

There is an underlying notion of "abstract formats" of type `'a format`, but the user operates at the level of "format fragments," which have type `('a, 'b) fragment` and are typically polymorphic in `'a` (where `'b` is instantiated to some type containing `'a`). Fragments are functions from formats to formats and can be composed freely using the infix function-composition operator (`o`). This form of format composition translates to a corresponding concatenation of the resulting output.

Fragments are composed from two kids of primitve fragments called elements and glue, respectively. An element is a fragment that consumes some argument (which thanks to the typing magic appears as a curried argument when the format gets executed). Glue are fragments that do not consume arguments but merely insert fixed text (fixed at format construction time) into the output.

There are also adjustment operations that pad, trim, or fit the output of entire fragments (primitive or not) to a given size.

Matthias Blume wrote the code for this module.

## Synopsis

    signature FORMAT_COMB
    structure FormatComb : FORMAT_COMB

## Interface

    type 'a format
    type ('a, 'b) fragment = 'a format -> 'b format

    type 'a glue = ('a, 'a) fragment
    type ('a, 't) element = ('a, 't -> 'a) fragment
    type 'a gg

    val format  : (string, 'a) fragment -> 'a

    val format' : (string list -> 'b) -> ('b, 'a) fragment -> 'a

    val using : ('t -> string) -> ('a, 't) element

    val int     : ('a, int) element
    val real    : ('a, real) element
    val bool    : ('a, bool) element
    val string  : ('a, string) element
    val string' : ('a, string) element
    val char    : ('a, char) element
    val char'   : ('a, char) element

    val int'  : StringCvt.radix   -> ('a, int) element
    val real' : StringCvt.realfmt -> ('a, real) element

    val list   : ('a, 'x) element -> ('a, 'x list) element
    val option : ('a, 'x) element -> ('a, 'x option) element
    val seq    : (('x * 'a gg -> 'a gg) -> 'a gg -> 's -> 'a gg)
    	     -> 'a glue
    	     -> ('a, 'x) element
    	     -> ('a, 's) element

    val glue : ('a, 't) element -> 't -> 'a glue

    val elem : ('t -> 'a glue) -> ('a, 't) element

    val nothing :           'a glue
    val text    : string -> 'a glue
    val sp      : int ->    'a glue
    val nl      :           'a glue
    val tab     :           'a glue

    val listg   : ('t -> 'a glue) -> ('t list -> 'a glue)
    val optiong : ('t -> 'a glue) -> ('t option -> 'a glue)

    val seqg   : (('x * 'a gg -> 'a gg) -> 'a gg -> 's -> 'a gg)
    	     -> 'a glue
    	     -> ('x -> 'a glue)
    	     -> 's -> 'a glue

    type place
    val left   : place
    val center : place
    val right  : place

    val pad  : place -> int -> ('a, 't) fragment -> ('a, 't) fragment
    val trim : place -> int -> ('a, 't) fragment -> ('a, 't) fragment
    val fit  : place -> int -> ('a, 't) fragment -> ('a, 't) fragment

    val padl : int -> ('a, 't) fragment -> ('a, 't) fragment
    val padr : int -> ('a, 't) fragment -> ('a, 't) fragment

## Description

`type 'a format`

  An abstract type; values of this type are internal to the implementation.

`type ('a, 'b) fragment = 'a format -> 'b format`

  A fragment is a function from formats to formats.

`type 'a glue = ('a, 'a) fragment`

  A `glue` fragment insertes text into the output without consuming and argument.

`type ('a, 't) element = ('a, 't -> 'a) fragment`

  An `element` fragment consumes an argument of type `'t`, converts it to a string, and inserts the result in the output.

`type 'a gg`

  An abstract helper type that is internal to the implementation.

`val format : (string, 'a) fragment -> 'a`

  `format fmt` returns a format function as defined by the expression `fmt` that will produce a string result when applied to the necessary arguments.

`val format' : (string list -> 'b) -> ('b, 'a) fragment -> 'a`

  `format' consumer fmt` returns a format function as defined by the expression `fmt` that will invoke the `consumer` on the list of strings produced by formating when applied to the necessary arguments. (The argument to the `consumer` is a string list to avoid premature string concatenation in the implementation). Note that the `format` function can be defined in terms of `format'` as follows:

    val format = format' String.concat

      (* Make a type-specific element given a toString function for this type *)
    `[.kw]#val# using : ('t \-> string) \-> ('a, 't) element`::
      `using tos` returns an element fragment that represents the given
      "value-to-string" conversion.

`val int : ('a, int) element`

  an element fragment for formating integers; it is equivalent to the expression `using Int.toString`.

`val int' : StringCvt.radix -> ('a, int) element`

  `int' radix` returns an element fragment for formating integers in the specified `radix`. It is equivalent to the expression `using (Int.fmt radix)`.

`val real : ('a, real) element`

  an element fragment for formating reals; it is equivalent to the expression `using Real.toString`.

`val real' : StringCvt.realfmt -> ('a, real) element`

  `real' rf` returns an element fragment for formating reals with the specified format. It is equivalent to the expression `using (Real.fmt rf)`.

`val bool : ('a, bool) element`

  an element fragment for formating booleans; it is equivalent to the expression `using Bool.toString`.

`val string : ('a, string) element`

  an element fragment for formating raw strings; it is equivalent to the expression `using (fn x ⇒ x)`.

`val string' : ('a, string) element`

  an element fragment for formating strings with escapes; it is equivalent to the expression `using String.toString`.

`val char : ('a, char) element (* using String.str *)`

  an element fragment for formating raw characters; it is equivalent to the expression `using String.str`.

`val char' : ('a, char) element (* using Char.toString *)`

  an element fragment for formating characters with escapes; it is equivalent to the expression `using Char.toString`.

      (* "polymorphic" elements *)

`val list : ('a, 'x) element -> ('a, 'x list) element (* "[", ", ", "]" *)`

  `list elemFmt` returns an element fragment that formats lists of items using the `elemFmt` element fragment to format items. The list will be enclosed in brackets (“`[`” “`]`”) with elements separated by commas.

`val option : ('a, 'x) element -> ('a, 'x option) element`

  `option elemFmt` returns an element fragment that formats optional items using the `elemFmt` element fragment to format the item value. For an argument of `NONE`, the string `"NONE"` is returned, while for an argument of `SOME v`, the string `"SOME(s)"` is returned, where `s` is the result of formatting `v` using `elemFmt`.

`val seq : (('x * 'a gg -> 'a gg) -> 'a gg -> 's -> 'a gg) -> 'a glue -> ('a, 'x) element -> ('a, 's) element`

  something

`val glue : ('a, 't) element -> 't -> 'a glue`

  `glue fmt arg` returns a glue element that renders as the string that results from using `fmt` to convert `arg` to a string.

`val elem : ('t -> 'a glue) -> ('a, 't) element`

  `elem glueGen` returns an element for rendering arguments to the `glueGen` function. This function is the inverse of `glue` and is useful for extending the set of combinators.

`val nothing : 'a glue`

  A glue fragment that renders as the empty string.

`val text : string -> 'a glue`

  `text s` returns a glue fragment that renders as the text `s`.

`val sp : int -> 'a glue`

  `sp n` returns a glue fragment that renders as `n` space characters.

`val nl : 'a glue`

  A glue fragment that renders as a newline character.

`val tab : 'a glue`

  A glue fragment that renders as a tab character.

`val listg : ('t -> 'a glue) -> ('t list -> 'a glue)`

  something

`val optiong : ('t -> 'a glue) -> ('t option -> 'a glue)`

  something

`val seqg : (('x * 'a gg -> 'a gg) -> 'a gg -> 's -> 'a gg) -> 'a glue -> ('x -> 'a glue) -> 's -> 'a glue`

  something

`type place`

  An abstract type that represents how to pad or trim of string.

`val left : place`

  Pad or trim the left side of a string.

`val center : place`

  Pad or trim both sides of a string.

`val right : place`

  Pad or trim the left side of a string.

`val pad : place -> int -> ('a, 't) fragment -> ('a, 't) fragment`

  `pad place n frag` wraps the fragment `frag` with padding to bring the total with to no fewer than `n` characters. The `place` specifies where padding spaces will be added. Padding never reduces the size of the result.

`val trim : place -> int -> ('a, 't) fragment -> ('a, 't) fragment`

  `trim place n frag` wraps the fragment `frag` with a trimming operation to bring the total with to no more than `n` characters. The `place` specifies where trimming occurs. Trimming never increases the size of the result.

`val fit : place -> int -> ('a, 't) fragment -> ('a, 't) fragment`

  `fit place n frag` wraps the fragment `frag` with an operation that guarantees the result will be exactly `n` characters by either padding or trimming as necessary.

`val padl : int -> ('a, 't) fragment -> ('a, 't) fragment`

  `padl n frag` is equivalent to `pad left n frag`.

`val padr : int -> ('a, 't) fragment -> ('a, 't) fragment`

  `padr n frag` is equivalent to `pad right n frag`.

## Examples

Here are examples on how to use this facility.

    format nothing          (* ==> "" *)
    format int 1234         (* ==> "1234" *)

    format (text "The square of " o int o text " is " o int o text ".") 2 4
                            (* ==> "The square of 2 is 4." *)

    format (int o bool o char) 1 true #"x"
                            (* ==> "1truex" *)

    format (glue string "glue vs. " o string o glue int 42 o sp 5 o int)
           "ordinary text " 17
                            (* ==> "glue vs. ordinary text 42     17" *)

and here are examples of how the `pad`/`trim`/`fit` functions work.

    format (pad left 6 int) 1234        (* ==> "  1234" *)
    format (pad center 6 int) 1234      (* ==> " 1234 " *)
    format (pad right 6 int) 1234       (* ==> "1234  " *)
    format (trim left 2 int) 1234       (* ==> "34"     *)
    format (trim center 2 int) 1234     (* ==> "23"     *)
    format (trim right 2 int) 1234      (* ==> "12"     *)
    format (fit left 3 int) 12          (* ==> " 12"    *)
    format (fit left 3 int) 123         (* ==> "123"    *)
    format (fit left 3 int) 1234        (* ==> "234"    *)

## See Also

`Format`, The Util Library

SML/NJ Version 110.99.6.1 (October 25, 2024)
Last updated 2024-10-25 16:47:18 UTC