[MLton] printf via fold

Stephen Weeks MLton@mlton.org
Fri, 2 Sep 2005 13:36:25 -0700


> I also did some experiments on FRU/OA (last changes seem to be dated
> the 25th), but forgot about it. Here are snippets of the code I came
> up with:
...
>   val r = <$arg#a 1 arg#b 2.0 arg#c "3"$> {a=0, b=0.0, c="0"}

Neat.  These discussions are opening my eyes to new programming
styles.

> OTOH, it doesn't seem imperative to expose $ at the top level, because it
> isn't an infix operator and it could just as well be bound in each module
> that uses the technique.  Of course, exposing $ at the top level saves some
> work. In other words, instead of
> 
>    Library code:
>      fun $ (*...*)
>      structure DSL = struct (* no $ *) ... end
> 
>    User code:
>      let open DSL in ... $ ... end
> 
> you could have
> 
>    Library code:
>      structure DSL = struct fun $ (*...*) ... end
> 
>    User code:
>      let open DSL in ... $ ... end
> 
> and the user code stays the same.

There is also a library cost in the signature.

In the client code, another situation I'm thinking of is where there
is only a single reference to the module, in which case going to "let
open DSL ..." is a syntactic cost.

   DSL.foo ...<no DSL> ... DSL.$

vs 

   let open DSL in foo ... <no DSL> ... $ end

vs

   DSL.foo ...<no DSL> ... $

In this situation, having $ is nice.

> >       fun step_1_0 z =
> >          Foldr.step1 (fn (b, (g, r)) => (g, fn a => r (g (b, a)))) z
...
> >   makeFold (a, g, f) 
> >            (step_1_0 h1) b1 (step_1_0 h2) b2 ... (step_1_0 hn) bn $ 
> >   === g (hn bn, ... g (h2 b2, g (h1 b1, a))
...
> The above definition of step_1_0 seems to be wrong. Shouldn't it be:
> 
>         fun step_1_0 h =
>            Foldr.step1 (fn (b, (g, r)) => (g, fn a => r (g (h b, a))))

Yes.  Good catch.  At least I got the equations right :-).

> Also, I wonder if it would make sense to use curried functions and
> to make it so that the "inline argument" (to step1, step_1_0, and
> step_1_1) is used "inline" (immediately) rather than after applying
> $. This would allow you to use partial application (evaluation) to
> avoid repeated computation

The makes sense in the case of step_1_0 and step_1_1.  I'm not sure
about Fold{,r}.step1, though -- do you see the possibility to avoid
repeat computation there or are you just going for
consistency/concision?  If there is no staging, then I'd argue for
keeping the tupled argument, so that the types aren't deceiving.

> The end of this message contains an implementation of this idea and
> a simple Scanf implementation that uses the (curried) Fold library.

Nice.  It would be good if the product syntax allowed some easy way to
denote products with zero or one component, but I can't see anything
better than what you did.
 
> Looking at the Ocaml Printf documentation, it seems that the simpler thing
> is the wrong thing to do:
> 
>   http://caml.inria.fr/pub/docs/manual-ocaml/libref/Printf.html
> 
> In other words, the warning would disappear from the documentation (and
> the gotcha, of course) if printing was delayed.

Yep.  I wonder why they do it that way, especially given that they
have compiler support for their approach.  Probably just historical
reasons.

> > In any case, MLton should still do the right thing.
> 
> Is there a simple way to verify this?

The way I do it is to compile some examples with one or more -keep
flags; {ssa,ssa2,rssa}.  Then I look at the IL and see that everything
got simplified away.  I don't know if that counts a simple :-).