Home My Page Projects Code Snippets Project Openings SML/NJ
Summary Activity Forums Tracker Lists Tasks Docs Surveys News SCM Files

SCM Repository

[smlnj] Annotation of /sml/trunk/src/fc/format-combinators-sig.sml
ViewVC logotype

Annotation of /sml/trunk/src/fc/format-combinators-sig.sml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1190 - (view) (download)

1 : blume 1190 (* format-combinators-sig.sml
2 :     *
3 :     * Well-typed "printf" for SML, aka "Unparsing Combinators".
4 :     * This code was written by Matthias Blume (2002). Inspiration
5 :     * obtained from Olivier Danvy's "Functional Unparsing" work.
6 :     *
7 :     * (C) 2002, Lucent Technologies, Bell Labs
8 :     *
9 :     *
10 :     * Description:
11 :     *
12 :     * The idea is to use combinators for constructing something akin to
13 :     * the format string of C's printf function. The difference is, however,
14 :     * that our formats aren't strings. Instead, format( fragment)s have
15 :     * meaningful types, and passing them to function "format" results
16 :     * in a curried function whose arguments have precisely the types that
17 :     * correspond to argument-consuming parts of the format. (Such
18 :     * argument-consuming parts are similar to the %-specifications of printf.)
19 :     *
20 :     * Here is how the typing works: There is an underlying notion of
21 :     * "abstract formats" of type 'a format. However, the user operates
22 :     * at the level of "format fragments" which have type ('a, 'b)
23 :     * fragment and are typically polymorphic in 'a (where 'b is
24 :     * instantiated to some type containing 'a). Fragments are
25 :     * functions from formats to formats and can be composed freely using
26 :     * the function composition operator 'o'. This form of format
27 :     * composition translates to a corresponding concatenation of the
28 :     * resulting output.
29 :     *
30 :     * Fragments are composed from two kids of primitve fragments called
31 :     * "elements" and "glue", respectively. An "element" is a fragment that
32 :     * consumes some argument (which thanks to the typing magic appears as a
33 :     * curried argument when the format gets executed). As "glue" we refer
34 :     * to fragments that do not consume arguments but merely insert fixed
35 :     * text (fixed at format construction time) into the output.
36 :     *
37 :     * There are also adjustment operations that pad, trim, or fit the output
38 :     * of entire fragments (primitive or not) to a given size.
39 :     *
40 :     * A number of elements and some glue has been predefined. Here are
41 :     * examples on how to use this facility:
42 :     *
43 :     * open FormatCombinators
44 :     *
45 :     * format nothing ==> ""
46 :     *
47 :     * format int ==> fn: int -> string
48 :     * format int 1234 ==> "1234"
49 :     *
50 :     * format (t"The square of " o int o t" is " o int o t".")
51 :     * ==> fn: int -> int -> string
52 :     * format (t"The square of " o int o t" is " o int o t".") 2 4
53 :     * ==> "The square of 2 is 4."
54 :     *
55 :     * format (int o bool o char) ==> fn : int -> bool -> char -> string
56 :     * format (int o bool o char) 1 true #"x"
57 :     * ==> "1truex"
58 :     *
59 :     * format (glue string "glue vs. " o string o glue int 42 o sp 5 o int)
60 :     * "ordinary text " 17
61 :     * ==> "glue vs. ordinary text 42 17"
62 :     *
63 :     * Fragments can be padded, trimmed, or fitted to generate text pieces of
64 :     * specified sizes. Padding/trimming/fitting may be nested.
65 :     * The operations are parameterized by a place (left, center, right) and
66 :     * a width. Padding never shrinks strings, trimming never extends
67 :     * strings, and fitting is done as necessary by either padding or trimming.
68 :     * Examples:
69 :     *
70 :     * format (pad left 6 int) 1234 ==> " 1234"
71 :     * format (pad center 6 int) 1234 ==> " 1234 "
72 :     * format (pad right 6 int) 1234 ==> "1234 "
73 :     * format (trim left 2 int) 1234 ==> "34"
74 :     * format (trim center 2 int) 1234 ==> "23"
75 :     * format (trim right 2 int) 1234 ==> "12"
76 :     * format (fit left 3 int) 12 ==> " 12"
77 :     * format (fit left 3 int) 123 ==> "123"
78 :     * format (fit left 3 int) 1234 ==> "234"
79 :     *
80 :     * Nesting:
81 :     *
82 :     * format (pad right 20 (int o pad left 10 real) o t"x") 12 22.3
83 :     * ==> "12 22.3 x"
84 :     *)
85 :     signature FORMAT_COMBINATORS = sig
86 :    
87 :     (* We reveal "fragments" to be functions from abstract formats
88 :     * to abstract formats. This is to make sure we can use function
89 :     * composition on them. *)
90 :     type 'a format
91 :     type ('a, 'b) fragment = 'a format -> 'b format
92 :    
93 :     (* Two primitive kinds of fragments: Glue inserts some text
94 :     * into the output without consuming an argument. Elements
95 :     * insert text corresponding to some (curried) argument into
96 :     * the output: *)
97 :     type 'a glue = ('a, 'a) fragment
98 :     type ('a, 't) element = ('a, 't -> 'a) fragment
99 :    
100 :     (* Format execution... *)
101 :     (* 1. Simple version, produce final result as a string: *)
102 :     val format : (string, 'a) fragment -> 'a
103 :     (* 2. Complex version, take a receiver function that will
104 :     * be invoked with the final result. The result is
105 :     * still in non-concatenated form at this time.
106 :     * (Internally, the combinators avoid string concatenation
107 :     * as long as there is no padding/trimming/fitting going on.) *)
108 :     val format' : (string list -> 'b) -> ('b, 'a) fragment -> 'a
109 :    
110 :     (* Make a type-specific element given a toString function for this type *)
111 :     val using : ('t -> string) -> ('a, 't) element
112 :    
113 :     (* Instantiate 'using' for a few types... *)
114 :     val int : ('a, int) element (* using Int.toString *)
115 :     val real : ('a, real) element (* using Real.toString *)
116 :     val bool : ('a, bool) element (* using Bool.toString *)
117 :     val string : ('a, string) element (* using (fn x => x) *)
118 :     val string' : ('a, string) element (* using String.toString *)
119 :     val char : ('a, char) element (* using String.chr *)
120 :     val char' : ('a, char) element (* using Char.toString *)
121 :    
122 :     (* Parameterized elements... *)
123 :     val int' : StringCvt.radix -> ('a, int) element (* using (Int.fmt r) *)
124 :     val real' : StringCvt.realfmt -> ('a, real) element (* using(Real.fmt f) *)
125 :    
126 :     (* Generic "gluifier". *)
127 :     val glue : ('a, 't) element -> 't -> 'a glue
128 :    
129 :     (* Other glue... *)
130 :     val nothing : 'a glue (* null glue *)
131 :     val t : string -> 'a glue (* constant text glue *)
132 :     val sp : int -> 'a glue (* n spaces glue *)
133 :     val nl : 'a glue (* newline glue *)
134 :     val tab : 'a glue (* tabulator glue *)
135 :    
136 :     (* "Places" say which side of a string to pad or trim... *)
137 :     type place
138 :     val left : place
139 :     val center : place
140 :     val right : place
141 :    
142 :     (* Pad, trim, or fit to size n the output corresponding to
143 :     * a format fragment: *)
144 :     val pad : place -> int -> ('a, 't) fragment -> ('a, 't) fragment
145 :     val trim : place -> int -> ('a, 't) fragment -> ('a, 't) fragment
146 :     val fit : place -> int -> ('a, 't) fragment -> ('a, 't) fragment
147 :     end

root@smlnj-gforge.cs.uchicago.edu
ViewVC Help
Powered by ViewVC 1.0.0