[MLton] printf via fold

Vesa Karvonen vesa.karvonen@cs.helsinki.fi
Sat, 10 Sep 2005 14:42:57 +0300


Quoting Stephen Weeks <sweeks@sweeks.com>:
> 2. An extension of Fold that supports exception handling.
> 
> structure FoldHandle =
>    struct
>       structure E =
>          struct
>             datatype 'a t = E of exn | V of 'a
> 
>             fun wrap f = V (f ()) handle e => E e
>          end
>       
>       fun fold (v: 'a, {finish, handler}) =
>          Fold.fold (E.V v, fn E.E e => handler e | E.V v => finish v)
>          
>       fun step0 h (e, f) =
>          Fold.fold (case e of E.E _ => e | E.V a => E.wrap (fn () => h a), f)
> 
>       fun step1 h $ x = step0 (fn a => h (x, a)) $
>    end

Just a couple of quick notes. (I'm in a bit of hurry today.)

There is a problem in the above implementation of step0. Namely, the case
expression restricts the type of the output (value of the case expression)
to be the same as the type of the input (e). (I noticed this as I used
FoldHandle to implement a yet another version of Scanf.)

You could also turn some of the implementation into quite useful utilities.
The either datatype (probably familiar to most) provided by Haskell's
standard prelude is useful in situations like this when you need a sum
type.

  datatype ('a, 'b) either = LEFT of 'a | RIGHT of 'b
  fun either (fa, fb) = fn LEFT a => fa a
                         | RIGHT b => fb b

I've also found the "eval" function for evaluating thunks quite useful.

  fun eval th = LEFT (th ()) handle e => RIGHT e

I use it in my implementation of try(-in-unless) and a couple of other
places (like in my SRFI-45 implementation).

The rest is just an implementation of FoldHandle with the case expression
replaced by an application of either. I also use Fold.step0 to simplify the
implementation.

  fun pass x f = f x

  structure Fold =
     struct
        val fold = pass
        fun step0 h (a1, f) = fold (h a1, f)
        fun step1 h $ x = step0 (fn a => h (x, a)) $
     end

  structure FoldHandle =
     struct
        fun fold (v, {finish, handler}) =
            Fold.fold (LEFT v, either (finish, handler))

        fun step0 h $ =
            Fold.step0 (either (fn v => eval (fn () => h v), RIGHT)) $

        fun step1 h $ x = step0 (fn a => h (x, a)) $
     end

(The version I have in my utility library is slightly different, though.
I don't use a {finish, handler} record and I use currying in step1.)

-Vesa Karvonen