Functional Unparsing

Stephen Weeks MLton@sourcelight.com
Tue, 2 Jan 2001 15:06:31 -0800 (PST)


Over Christmas, I worked through the following technical report

   Functional Unparsing
   BRICS Technical Report RS 98-12
   Olivier Danvy, May 1998

It was an interesting demonstration of the power of the SML type system.  It
shows how to inductively construct a pretty printing function that (apparently)
accepts a variable number of arguments.  Here are some example uses from below,
all of which yield the string "abc".

      format (lit "abc")
      format string "abc"
      format (lit "a" o lit "b" o lit "c")
      format (string o string o string) "a" "b" "c"

I worked out a signature that helped me understand a lot better what's going
on.  I though you might find it interesting.  It would be interesting to look
into the kind of code MLton generates for such programs.  I think
monomorphisation + flow analysis + inlining simplifies a lot.

Here's the signature I made up and the implementation from the paper.  It would 
also be interesting if there were other implementations.  I couldn't think of
any.

--------------------------------------------------------------------------------

signature FORMAT =
   sig
      type ('a, 'b) t

      val eol: ('a, 'a) t
      val format: (string, 'a) t -> 'a
      val int: ('a, int -> 'a) t
      val list: ('a, 'b -> 'a) t -> ('a, 'b list -> 'a) t
      val lit: string -> ('a, 'a) t 
      val new: ('b -> string) -> ('a, 'b -> 'a) t
      val o: ('a, 'b) t * ('c, 'a) t -> ('c, 'b) t
      val string: ('a, string -> 'a) t
   end

structure Format:> FORMAT =
struct

type ('a, 'b) t = (string list -> 'a) * string list -> 'b

val new: ('b -> string) -> ('a, 'b -> 'a) t =
   fn toString => fn (k, ss) => fn b => k (toString b :: ss)

val lit: string -> ('a, 'a) t = fn s => fn (k, ss) => k (s :: ss)

val eol: ('a, 'a) t = fn z => lit "\n" z
   
val format: (string, 'a) t -> 'a = fn f => f (concat o rev, [])
   
val int: ('a, int -> 'a) t = fn z => new Int.toString z
   
val list: ('a, 'b -> 'a) t -> ('a, 'b list -> 'a) t =
   fn f => fn (k, ss) =>
   fn [] => k ("[]" :: ss)
    | x :: xs =>
	 let
	    fun loop xs ss =
	       case xs of
		  [] => k ("]" :: ss)
		| x :: xs => f (loop xs, ", " :: ss) x
	 in f (loop xs, "[" :: ss) x
	 end

val op o: ('a, 'b) t * ('c, 'a) t -> ('c, 'b) t =
   fn (f, g) => fn (k, ss) => f (fn ss => g (k, ss), ss)

val string: ('a, string -> 'a) t = fn z => new (fn s => s) z
   
end

open Format

val _ =
   if
      "abc" = format (lit "abc")
      andalso "abc" = format string "abc"
      andalso "abc" = format (lit "a" o lit "b" o lit "c")
      andalso "abc" = format (string o string o string) "a" "b" "c"
      andalso "[a, b, c]" = format (list string) ["a", "b", "c"]
      andalso "[1, 2, 3]" = format (list int) [1, 2, 3]
      then ()
   else raise Fail "bug"