Go to the first, previous, next, last section, table of contents.


The module Text: Convenient text manipulation

Before we describe the new syntax, we have to introduce a tiny module : Text (what an original name). With this module, every Caml value "is" a string. To understand what it means, let's define the "string content" S(x) of a value x. It is a string, defined by:

  1. if x is a string, S(x)=x
  2. if x is an int, a char, or a constant constructor of a concrete type, S(x)=""
  3. if x is a block {x1,...,xn}, S(x) is the concatenation S(x1)^...^S(xn)

Similarly, the "string components" SC(x) is defined by:

  1. if x is a string, SC(x)=[x]
  2. if x is an int, a char, or a constant constructor of a concrete type, SC(x)=[]
  3. if x is a block {x1,...,xn}, SC(x) is SC(x1)@...@SC(xn)

The module defines an opaque type Text.t with an "universal constructor" Text.repr: 'a -> t. The function Text.iter:(string -> unit) -> t -> unit applies a given function to the string components of a Text.t value. There are also two functions to compute S(x) and its length.

So, for instance, we have (oh yes, this is not well-typed; you have to add Text.repr everywhere):

  1. S("Hello world !") = "Hello world !"
    
  2. S(["Hello";(" ","World");Some " !"]) = "Hello world !"
    
  3. S([|"Hello"; 5; Some (5," "); "World !"|]) = "Hello world !"
    

Typically, an application builds its output by concatenating strings. This involves many copy operations so it may be quite slow. With the module Text, just put your substrings in a list (or better, an array: only one big block), and see it as a string with Text.repr. When you want to finally output the "string", use Text.iter or Text.to_string on the result.

Another feature of the module Text is the manipulation of "postponed texts". Suppose you want to create a text with a hole to be filled later, when an extra piece of information is available. A simple way to use a reference:


   let post = ref Text.empty in
   repr [Text.repr "Total size :"; Text.repr post];
   (* ... *)
   post := Text.repr (string_of_int (total_size ()));

Sometimes it is better to put the expression to be computed later at the position of the postponed text, then to activate it later; also, having a global reference is not easy to manage. You can do that:


   repr [Text.repr "Total size :"; Text.postponed "totalsize" (fun ()
-> Text.repr (string_of_int (total_size ())))];
   (* ... *)
   Text.activate "totalsize"

Here, "totalsize" identifies the delayed computation and Text.activate triggers the evaluation.


Go to the first, previous, next, last section, table of contents.