2008-02-02

Refining Pipelines

Trying to emulate a text-processing pipeline in a functional language with the intent of replacing shell-level programming is both natural and appealing. In Haskell, the lazy nature of the language makes this efficient as a bonus, as the streaming nature of Unix pipes is preserved.

Trying to do this naïvely in a strict language like OCaml results in programs that transform the entire contents of the file at each step, consuming unbounded amounts of memory and generating large quantities of garbage. The obvious enhancement is to process text files one line at a time.

I'll start with the line reader I blogged about in December.. The idea of that code was that to iterate over a file's lines, applying a user-supplied function to each one in turn. To give pipelines a more shell-like flavor, I'd like to abstract out the mechanics of opening and closing files, and working instead with filenames.

The higher-order RAII pattern is straightforward in a functional language:

let unwind ~(protect: 'a -> unit) f x =
  try
    let res = f x in
    protect x; res
  with e ->
    protect x; raise e

With this, I can define channel handlers protect the application of a user function to a channel:

let with_input_channel  f = unwind ~protect:close_in  f
and with_output_channel f = unwind ~protect:close_out f

To get it out of the way now, this is the line-writing function I'll need later:

let output_endline out s =
  output_string out s;
  output_char out '\n'

(In contrast to print_endline, this one writes to an output channel.) With this, I can define a version of the line iterator that takes a filename instead of an already-opened channel:

let read fname f = with_input_channel (iter_lines f) (open_in_bin fname)

The type of read is string -> (string -> unit) -> unit. It can be viewed not as an iterator but as a function returning a continuation monad.

Just as I did with read, I would like to abstract away the opening and closing of the output file, and expose an interface that only deals with file names. If the pipeline is to consume its input line by line, it is not immediate how that would work without opening the named file, appending the line to it and closing it afterwards, once for each line. So as a first approximation, write fname would have the shape:

let write fname =
  with_output_channel (fun out -> … ) (open_out_bin fname)

Somehow, lines would be delivered to with_output_channel's argument, to be written by output_endline:

let write fname =
  with_output_channel (fun out -> … output_endline out …)
    (open_out_bin fname)

The only sensible thing to do is to let output_endline out be the continuation of… something that would invoke it for each processed line. Abstracting out that "something":

let write fname f =
  with_output_channel (fun out -> f (output_endline out)) (open_out_bin fname)

But then I can write the argument in point-free style:

let (%) f g x = f (g x)

let write fname f =
  with_output_channel (f % output_endline) (open_out_bin fname)

The type of write is string -> ((string -> unit) -> 'a) -> 'a. This is analogous to the so-called runner in the continuation monad. Now we have that read "foo" has type (string -> unit) -> unit, which unifies with the type of the second argument to write "bar" with 'a equal to unit. This implies that write "bar" (read "foo") really copies files, one line at a time (actually, it normalizes line endings). There is really no need to prove this by equational reasoning; the types are a "free theorem" when interpreted in the continuation monad.

Now, it's clear that the combinator ( >> ) that would allow us to write read "foo" >> write "bar" is simply reverse application in operator form:

let (>>) x f = f x

Now, I would like to interpose some line-processing functions in the pipeline between read and write. For instance, a regular-expression global-replace-and-substitute (a.k.a. sed):

let replace = Str.global_replace % Str.regexp

It really works:

# replace "a+" "amas " "anaconda" ;;
- : string = "amas namas condamas "

Hocus-pocus. Also, the following is useful:

let escapeXML s =
  let b = Buffer.create (String.length s) in
  String.iter (function
  | '<' -> Buffer.add_string b "<"
  | '>' -> Buffer.add_string b ">"
  | '&' -> Buffer.add_string b "&"
  | '"' -> Buffer.add_string b """
  | c ->
    let d = int_of_char c in
    if d < 128 then Buffer.add_char b c else begin
      Buffer.add_string b "&#";
      Buffer.add_string b (string_of_int d);
      Buffer.add_string b ";"
    end) s;
  Buffer.contents b

So, now, how to stack these string -> string line-editors in the input-output pipeline? A general pipeline would look like this:

read "foo" ## led1 ## led2 … ## ledn ## write "bar"

where the ## are the relevant operators. One of these must be our ( >> ) applicator; and if n = 0, it must be either the first or the last. By the types of them, the read continuation looks simpler to compose; so let's build the transformations from the left and let it be applied en bloc to write:

read "foo" ## led1 ## led2 … ## ledn >> write "bar"

The chain of line-editors must stack on top the line-reading continuation; in other words, read "foo" will have to be the bottommost, last continuation applied to write "bar". We need ## to be left-associative; hence, let's call it ( |> ):

read "foo" |> stred1 |> stred2 … |> stredn >> write "bar"

So that ( |> ) takes a reader and a line-editor, and builds a filtered reader. This reader takes a continuation argument with type (string -> unit) -> unit. Tt is the string passed to it that must be filtered, and composition suffices:

let (|>) reader f k = reader (k % f)

On the other hand, a right-associative operator ( @> ) that would let us to write a right-to-left filtered output pipeline:

read "foo" >> stred1 @> stred2 … @> stredn @> write "bar"

is more complicated. First of all, it should take a filter and a writer and return a filtered writer:

let (@>) (f : string -> string) (writer : ((string -> unit) -> 'a) -> 'a)
  : ((string -> unit) -> 'a) -> 'a = …

The types should guide the refining of the operator. Given what they are, for now, let's make it ignore the filter and return the writer unmodified:

let (@>) (f : string -> string) (writer : ((string -> unit) -> 'a) -> 'a)
  : ((string -> unit) -> 'a) -> 'a = writer

This writer is applied a writing continuation of type (string -> unit) -> 'a. Abstracting it out:

let (@>) (f : string -> string) (writer : ((string -> unit) -> 'a) -> 'a)
  : ((string -> unit) -> 'a) -> 'a = fun (k : (string -> unit) -> 'a) -> writer k

This continuation gets passed, in turn, the continuation that actually writes each line out. Abstracting it out:

let (@>) (f : string -> string) (writer : ((string -> unit) -> 'a) -> 'a)
  : ((string -> unit) -> 'a) -> 'a = 
    fun (k : (string -> unit) -> 'a) -> writer (fun (w : string -> unit) -> k w)

Now we have a context where to apply the filter:

let (@>) (f : string -> string) (writer : ((string -> unit) -> 'a) -> 'a)
  : ((string -> unit) -> 'a) -> 'a = 
    fun (k : (string -> unit) -> 'a) -> writer (fun (w : string -> unit) -> k (w % f))

Eliminating the typing constraints:

let (@>) f writer k = writer (fun w -> k (w % f))

We can now write read "file.xml" >> replace "[<>]" "#" @> write "foo.txt" or read "file.xml" |> replace "[<>]" "#" >> write "foo.txt", whichever is more natural.

Codeless Language Module for OCaml

Since I couldn't find a ready-made syntax highlighting definition to edit OCaml code in BBEdit, I pieced together one from information and examples I found around. So, for the record, here is an OCaml Codeless Language Module that does the job:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>BBEditDocumentType</key>
  <string>CodelessLanguageModule</string>
  <key>BBLMColorsSyntax</key>
  <true/>
  <key>BBLMIsCaseSensitive</key>
  <true/>
  <key>BBLMKeywordList</key>
  <array>
    <string>and</string>
    <string>as</string>
    <string>assert</string>
    <string>asr</string>
    <string>begin</string>
    <string>class</string>
    <string>constraint</string>
    <string>do</string>
    <string>done</string>
    <string>downto</string>
    <string>else</string>
    <string>end</string>
    <string>exception</string>
    <string>external</string>
    <string>false</string>
    <string>for</string>
    <string>fun</string>
    <string>function</string>
    <string>functor</string>
    <string>if</string>
    <string>in</string>
    <string>include</string>
    <string>inherit</string>
    <string>initializer</string>
    <string>land</string>
    <string>lazy</string>
    <string>let</string>
    <string>lor</string>
    <string>lsl</string>
    <string>lsr</string>
    <string>lxor</string>
    <string>match</string>
    <string>method</string>
    <string>mod</string>
    <string>module</string>
    <string>mutable</string>
    <string>new</string>
    <string>object</string>
    <string>of</string>
    <string>open</string>
    <string>or</string>
    <string>parser</string>
    <string>private</string>
    <string>rec</string>
    <string>sig</string>
    <string>struct</string>
    <string>then</string>
    <string>to</string>
    <string>true</string>
    <string>try</string>
    <string>type</string>
    <string>val</string>
    <string>virtual</string>
    <string>when</string>
    <string>while</string>
    <string>with</string>
  </array>
  <key>BBLMLanguageCode</key>
  <string>Caml</string>
  <key>BBLMLanguageDisplayName</key>
  <string>Objective Caml</string>
  <key>BBLMScansFunctions</key>
  <true/>
  <key>BBLMSuffixMap</key>
  <array>
    <dict>
      <key>BBLMLanguageSuffix</key>
      <string>.ml</string>
    </dict>
    <dict>
      <key>BBLMLanguageSuffix</key>
      <string>.mli</string>
    </dict>
  </array>
  <key>Language Features</key>
  <dict>
    <key>Comment Pattern</key>
    <string><![CDATA[(?x:
(?> \(\* (?s: .*? ) (?> \*\) | \z ) )
    )]]></string>
    <key>Function Pattern</key>
    <string><![CDATA[(?x:
^[ \t]*
(?P<function>
  \b(?: let | and )\b [ \t]+
  (?: \brec\b [ \t]+ )?
  (?P<function_name>
    [A-Za-z]['0-9A-Za-z_]* | \([^)]+\)
  )
  (?:[ \t]+ [^ \t=]+)+ [ \t]*
  =
)
    )]]></string>
    <key>String Pattern</key>
    <string><![CDATA[(?x:
(?>
  (?<!') " (?: [^"\\] |
    (?P<escape>
      \\ (?: ['"ntbr\\] | \d{3} | x[0-9A-Fa-f]{2} )
    )
  )*
  (?> " | \z ) ) |
(?> ' (?: [^'\\] | (?P>escape) ) ' )
    )]]></string>
    <key>Identifier and Keyword Character Class</key>
    <string>\w</string>
    <key>Skip Pattern</key>
    <string><![CDATA[(?x:
(?> \(\* (?s: .*? ) (?> \*\) | \z ) )
|
(?> (?<!') " (?: [^"\\] | \\ (?: ['"ntbr\\] | \d{3} | x[0-9A-Fa-f]{2} ) )* " )
    )]]></string>
  </dict>
</dict>
</plist>

The function pattern could use some work, and the comments pattern doesn't recognize nested comments, but it's a start. I hope you will find it useful.

2008-01-28

GD on Leopard

As a once-again proud owner of a feline, I'm building my development environment. I've found an excellent walk-through for installing MySQL, and another instalation guide for GD (PDF) for PHP use. The MacBook Pro is the fastest machine I've ever used, bar none. Such a pleasure, well-worth the investment.

For the record, I'll mention the configure parameters I used to compile GD with full graphics support:

./configure --x-includes=/usr/X11/include \
  --x-libraries=/usr/X11/lib \
  --with-freetype=/usr/X11/

As in the linked-to tutorial, I assume you've installed the full X11 development libraries. With that, the result is:

** Configuration summary for gd 2.0.34:

   Support for PNG library:          yes
   Support for JPEG library:         yes
   Support for Freetype 2.x library: yes
   Support for Fontconfig library:   yes
   Support for Xpm library:          yes
   Support for pthreads:             yes

I hope somebody finds this useful.

2008-01-13

Predictor, Shmedictor

Scott Aaronson attempts to tackle the so-called Newcomb's Paradox, and discovers a new angle into it. To recapitulate quickly, the paradox poses a putative entity, the Predictor, which confronts everybody with to boxes: the first either has $1,000,000 in it or nothing, the second always contains $1,000. The catch is that the Predictor knows with perfect, inerrant foresight what your actions will be: if you choose to open only the first box, It will put the million inside it; if you choose to open both boxes, It will leave it empty. This way, you have no rational choice based on probabilistic expectations, as both alternatives of an either-or analysis contradicts the other possibility.

Aaronson's tackle on this is that the Predictor need not be omniscient, just a detailed-enough simulation of yourself that "runs" you forward enough to predict your choice and set up the boxes accordingly. Cosma Shalizi's view is that the paradox stems from trying to reason about the Predictor from the point of view of our own finiteness. I call bullshit: my view on this is that the Paradox is not, but just the result of a categorical mistake. I read the list of attempts at cracking it and cannot help but think "where's Wittgenstein when we need him?"

What I find bogus about the Paradox is the insistence to take at face value its setup in a probabilistic framework. To me it is obvious that the Predictor isn't such, but just a deterministic function from choice to payoff. Put it in these terms, it is wrong to use expectations to tackle the problem; for me, the right avenue of attack is to treat it as a maximization problem.

In other words, even if the problem poser insists on accounting for the phenomenological reality of the Predictor as defined by the problem, I can still sidestep the issue of Its nature and behave as if It does not exist as postulated. That is, it is irrelevant if the Predictor predicts my moves, as Aaronson chooses to explain, as It acts consistently independently of the chooser. Its actions are only dependent on the chooser's choice, hence, It is still a deterministic function from choice to payoff. The choice has a finite domain isomorphic to the booleans (open the second box or not):

choicepayoff
false$1,000,000
true$1,000

with maximum value at false.

Don't open that second box, silly!

2008-01-10

Pointless Polymorphism

The echo chamber started resonating with Albert Y. C. Lai's post. The point of it is that function composition is a more pervasive paradigm (or abstraction device, rather) than just higher-order functional programming. In Haskell, point-freeness is a usual stylistic device (in the literary sense); in the ML family of languages it is seldom seen.

Andreas Farre pitched in with the composition combinators defined in F#. F# shares with OCaml the syntactic quirk of arbitrary infix operators built from fixed operator characters, whose precedence and associativity are fixed in advance and depend on the first character in the operator name. For instance, in OCaml, operators starting with | are left-associative, but those starting with @ are right-associative and at a higher precedence level. I pointed out on the discussion over at reddit that this, coupled with the value restriction makes point-free style in ML-family languages (including OCaml) inconvenient.

Aside: If you don't see the point of the puns, you're not alone: I don't either, but it seems that any discussion of point-free programming induces everybody to start shooting bad puns point-blank. I'll make an effort to try and stop it.

What this means from a practical standpoint is that η-conversion doesn't quite work symmetrically: whereas f is always equivalent to fun x -> f x, the converse is not true. For example, let id x = x be the identity function, with type α → α. Then, fun x -> List.map id x has type α list → α list, but the η-converted, point-free List.map id does not: the result type in OCaml is '_a list → '_a list, where the type variables are monomorphic and unbound. The first application of this function to an argument of concrete type T will fix the point-free function's type to be TT, for ever.

I like to define the composition operator in OCaml as:

let ( % ) f g x = f (g x)

since % is relatively lightweight and it is not defined elsewhere in the standard library. The purportedly more obvious alternative would be using @, as it is reminiscent of the ring operator ∘ that usually denotes composition in Mathematics. It has two inconvenients, however: it is already in use to denote list concatenation, and it has the wrong precedence for composition, namely right-to-left.

With this, we can see that the value restriction won't let us define a point-free polymorphic reverse sort:

# let revsort = List.rev % List.sort Pervasives.compare ;;
val revsort : '_a list -> '_a list = <fun>

Even though compare is polymorphic on its arguments, the defined function is not:

# revsort [1;2;3;4] ;;
- : int list = [4; 3; 2; 1]

# revsort ;;
- : int list -> int list = <fun>

The first application of revsort to a value fixes its type to that of the value. The fix is to η-expand the function and lose point-freeness:

# let revsort x = (List.rev % List.sort Pervasives.compare) x ;;
val revsort : 'a list -> 'a list = <fun>

Here's where F# operators |> and >> are handy, as they build the composition applied to the point, in the right order (left to right). There is, however, a case where point-free programming remains applicable and expressive under a Hindley-Milner type discipline: when composing monomorphic functions.

Browsing type specimens, I came across some pangrams I didn't know. They looked like pangrams, but I didn't want to check every letter to see if they effectively were. Programming to the rescue! First, I needed to define the isomorphism between strings and lists of characters, which is built-in in Haskell but not in OCaml:

let unpack s =
  let r = ref [] in
  String.iter (fun c -> r := c :: !r) s;
  List.rev !r

let pack l =
  let b = Buffer.create 16 in
  List.iter (Buffer.add_char b) l;
  Buffer.contents b

These are imperative for efficiency, but they need not be. A predicate to test if a character is alphabetic is quite easy:

let is_alpha = function 'A' .. 'Z' | 'a' .. 'z' -> true | _ -> false

Range and or matchings are compiled very efficiently, so this is almost as good as a table lookup. Many letters will be repeated many times; the easiest way to filter duplicates is to assume that the list is sorted, and the repetitions occur in contiguous blocks:

let rec unique = function
  [] | [_] as l -> l
| x :: (y :: _ as l) -> if x = y then unique l else x :: (unique l)

The alternative would be the equivalent to Haskell's nub that removes duplicates anywhere on the list, retaining the first and respecting the order of the elements in the original list. This constitutes the generic scaffolding. For convenience, I'll alias a couple of things:

open List
open String

let sort l = List.sort Pervasives.compare l

Note that the sort is polymorphic, as it is a syntactic value (which happens to be functional). And now for the punchline, we get to the point of closing:

let letters = pack % unique % sort % filter is_alpha % unpack % lowercase

The use of monomorphic lowercase, unpack and is_alpha forces the type inferencer to derive monomorphic types for filter, sort and unique, and thus making the value restriction irrelevant here. The processing pipeline is as concisely and elegantly expressed as any in Haskell (or in Unix shell, for that matter). With that, we can test for panalphabets like this:

let is_panalphabet = (=) 26 % length % letters

For instance (à ta santé, OCaml!):

# is_panalphabet "Portez ce vieux whisky au juge blond qui fume" ;;
- : bool = true

(no diacritics were harmed in this production). Or:

# List.map is_panalphabet [
"jaded zombies acted quaintly but kept driving their oxen forward" ;
"the quick brown fox jumps over the lazy dog" ;
"pack my box with five dozen liquor jugs" ;
"sphinx of black quartz judge my vow" ;
];;
- : bool list = [true; true; true; true]

So, there is a use for point-free programming in OCaml, especially when dealing with type-based DSLs, as the functions operating on values are usually monomorphic. And this is the point I wanted to make.