Re: string variables in Printf.* calls: Bug, or lack of understanding?

From: Xavier Leroy (xleroy@pauillac.inria.fr)
Date: Tue Feb 18 1997 - 17:39:40 MET


From: Xavier Leroy <xleroy@pauillac.inria.fr>
Message-Id: <199702181639.RAA28850@pauillac.inria.fr>
Subject: Re: string variables in Printf.* calls: Bug, or lack of understanding?
In-Reply-To: <199702131831.NAA22295@abetti> from "T. Kurt Bond" at "Feb 13, 97 01:31:02 pm"
To: thomas.k.bond@cpmx.saic.com (T. Kurt Bond)
Date: Tue, 18 Feb 1997 17:39:40 +0100 (MET)

> Objective Caml version 1.03
>
> [1] # let s = "%f" in Printf.printf s 10.5;;
> Characters 30-31:
> This expression has type string but is here used with type
> ('a, out_channel, unit) format
>
> [2] # Printf.printf "%f" 10.5;;
> 10.500000- : unit = ()
>
> I don't understand why the variable s, which is bound to a string value,
> causes the error in statement 1, while using a literal string in
> statement 2 works as expected. Can anyone explain? Or is this a bug?

It's a feature: format strings are typed specially in order to make
printf type-safe. Look at the type for Printf.printf: it's

        ('a, out_channel, unit) format -> ...

and not

        string -> ...

A format string is typed specially and receives a type of the form
(t1, t2, t3) format, for suitable types t1, t2, t3. For instance, the
format string ``%f %d'' has type

        (float -> int -> 'b, 'c, 'b) format

which, when passed as first argument to Printf.printf, gives the
return type

        float -> int -> unit

which ensures that the next two arguments are a float and an integer,
as expected.

Now comes the dirty hack. In an ideal world, there would be a
different syntax for string literals and for format literals, so that
the type-checker would always know whether to type the literal as a
string or as a format.

For backward compatibility, and because we are somehow running out of
quote characters, format literals have actually the same syntax as
strings, and it's the type expected by the context of the literal that
says whether to type it as a string or as a format. In some sense, the
syntax for string literals is overloaded for format literals as well.

In particular, when a string literal occur as first argument to
Printf.printf (your example [2]), its expected type is ('a,
out_channel, unit) format and thus the type-checker types it as a
format literal. In other contexts, such as your example [1] above,
there is no expected type and the literal is therefore typed as a
string.

However, you can force the correct behavior with a type constraint:

        let s = ("%f" : ('a, 'b, 'c) format) in Printf.printf s 10.5;;

You can actually go quite far this way, and use formats as first-class
values (pass them as arguments to functions, etc) -- all in a
type-safe way. Just remember to put a type constraint around format
literals if the context does not already expect a format type.

- Xavier Leroy



This archive was generated by hypermail 2b29 : Sun Jan 02 2000 - 11:58:09 MET