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 /smlnj-lib/trunk/Util/format-comb-sig.sml
ViewVC logotype

Annotation of /smlnj-lib/trunk/Util/format-comb-sig.sml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2921 - (view) (download)

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

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