caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* AW: Format.sprintf and "%a"
@ 2000-11-23 15:13 jim.rauser
  2000-11-25 15:43 ` Pierre Weis
  2000-11-28 12:52 ` thierry BRAVIER
  0 siblings, 2 replies; 3+ messages in thread
From: jim.rauser @ 2000-11-23 15:13 UTC (permalink / raw)
  To: caml-list

Pierre Weis wrote:
>
> Nothing more than the reported type error: your functions  are
> designed to return unit (they work by side effect) not string (which
> would mean they are functional), and this is obviously uncompatible.
>
> let s =
>  Format.fprintf str_formatter "Test: %a@." fmt_foo Foo;
>  Format.flush_str_formatter ();;
> val s : string = "Test: Foo\n"

Okay, I found the definition of the type "format" in pervasives.mli,
so I understand the type error now.  But I then tried to partially
apply Format.fprintf to Format.str_formatter:

  let fsprintf = Format.fprintf Format.str_formatter;;
  val fsprintf : ('_a, Format.formatter, unit) format -> '_a = <fun>

Looks good, except for the ominous "_" in front of the type variable
(what does it mean, anyway?).  But:

  let fmt_string f s = Format.fprintf f "%s" s;;
  fsprintf "%a@." fmt_string "foo";;
                  ----------
  This expression has type Format.formatter -> string -> unit
  but is here used with type (string -> 'a, Format.formatter, unit) format

This type error also makes sense.  What I don't understand is,
why it *does* work with Format.fprintf, that is, how the compiler
convinces itself that an argument list like ["%a" fmt_string "foo"]
unifies with the type [('a, Format.formatter, unit) format]?  I don't
recall seeing anything else in the manual that talks about functions
with variable arity; is there some extra-linguistic magic going on here?

Thanks again,

Jim

------------------------------------------------------------------------
Jim Rauser                                      mailto:rauser@qcentic.de
Qcentic GmbH
Max-Planck-Str. 39a                                 Tel: 02234/950 36 16
D-50858 Koeln                                        Fax: 02234/27 19 17



^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: AW: Format.sprintf and "%a"
  2000-11-23 15:13 AW: Format.sprintf and "%a" jim.rauser
@ 2000-11-25 15:43 ` Pierre Weis
  2000-11-28 12:52 ` thierry BRAVIER
  1 sibling, 0 replies; 3+ messages in thread
From: Pierre Weis @ 2000-11-25 15:43 UTC (permalink / raw)
  To: jim.rauser; +Cc: caml-list

> Pierre Weis wrote:
> >
> > Nothing more than the reported type error: your functions  are
> > designed to return unit (they work by side effect) not string (which
> > would mean they are functional), and this is obviously uncompatible.
> >
> > let s =
> >  Format.fprintf str_formatter "Test: %a@." fmt_foo Foo;
> >  Format.flush_str_formatter ();;
> > val s : string = "Test: Foo\n"
 
Jim Rauser mailto:rauser@qcentic.de answered:

> Okay, I found the definition of the type "format" in pervasives.mli,
> so I understand the type error now.  But I then tried to partially
> apply Format.fprintf to Format.str_formatter:
> 
>   let fsprintf = Format.fprintf Format.str_formatter;;
>   val fsprintf : ('_a, Format.formatter, unit) format -> '_a = <fun>
> 
> Looks good, except for the ominous "_" in front of the type variable
> (what does it mean, anyway?).  But:

Have a look at the FAQ:
http://pauillac.inria.fr/caml/FAQ/FAQ_EXPERT-eng.html

the item ``What means '_a ? '' could be relevant.

>   let fmt_string f s = Format.fprintf f "%s" s;;
>   fsprintf "%a@." fmt_string "foo";;
>                   ----------
>   This expression has type Format.formatter -> string -> unit
>   but is here used with type (string -> 'a, Format.formatter, unit) format
> 
> This type error also makes sense.

No, I just don't understand it, and I was not able to reproduce it:

# let fsprintf = Format.fprintf Format.str_formatter;;
val fsprintf : ('_a, Format.formatter, unit) format -> '_a = <fun>
# let fmt_string f s = Format.fprintf f "%s" s;;
val fmt_string : Format.formatter -> string -> unit = <fun>
# fsprintf "%a@." fmt_string "foo";;
- : unit = ()
# Format.flush_str_formatter ();;
- : string = "foo\n"

However, the usual semantics of '_a still applies:

#fsprintf;;
- : ((Format.formatter -> string -> unit) -> string -> unit,
     Format.formatter, unit)
    format -> (Format.formatter -> string -> unit) -> string -> unit
= <fun>

> What I don't understand is, why it *does* work with Format.fprintf,
> that is, how the compiler convinces itself that an argument list
> like ["%a" fmt_string "foo"] unifies with the type [('a,
> Format.formatter, unit) format]?  I don't recall seeing anything
> else in the manual that talks about functions with variable arity;
> is there some extra-linguistic magic going on here?

Yes. Here are some hints about this magic.

First of all, the typechecker has an extra rule for strings: the
auxiliary rule states that if the typechecking context needs to
consider a string constant as a value of type ('a, 'b, 'c) format then
this is not considered as an error.  Furthermore the typechecker
analyses the contents of the constant format strings to find out what
can be the types of the arguments of printf. For instance:

# ("" : ('a, 'b, 'c) format);;
- : ('a, 'b, 'a) format = <abstr>

# let fmt = ("print an int: %i" : ('a, 'b, 'c) format);;
val fmt : (int -> 'a, 'b, 'a) format = <abstr>

In case of an application of printf the typechecker unifies the result
type of the application to the one specified by the given format
string (unit in the case of format for printf, string in the case of
format for sprintf).

# let f i = Printf.fprintf stdout fmt i;;
val f : int -> unit = <fun>
# f 3;;
print an int: 3- : unit = ()

You have to be careful with partial applications of printf-like
functions to format strings, since those applications are really
partial applications! (Meaning that side-effects may happen at each
new argument passed to the function)!

For instance, the format "print an int: %i", is in some sense
equivalent to the functional expression:

 print_string "print an int: "; function i -> print_int i

Hence the following behaviours:

# Printf.fprintf stdout fmt;;
print an int: - : int -> unit = <fun>

and also:

# let f = Printf.fprintf stdout fmt;;
print an int: val f : int -> unit = <fun>

More generally, a ('a, 'b, 'c) format string value applied to a printf
like function means that:

-- the resulting function will accept arguments with type 'a
-- embedded %a applications will accept values of type 'b as first
   argument
-- the resulting function will return values of type 'c

# Printf.sprintf;;
- : ('a, unit, string) format -> 'a = <fun>

# Printf.sprintf "%a";;       
- : (unit -> '_a -> string) -> '_a -> string = <fun>

# Printf.fprintf;;
- : out_channel -> ('a, out_channel, unit) format -> 'a = <fun>

# Printf.fprintf stdout "%a";;
- : (out_channel -> '_a -> unit) -> '_a -> unit = <fun>

As you can see, embedded function calls must have the same 'b and 'c
parameters as the format they are called from (meaning that since
Printf.fprintf stdout takes a ('a, out_channel, unit) format as format
string, it can only accept %a convertion specifications with functions
f having type out_channel -> 'a -> unit).

PS: May be having format strings as lexical entities per se may help
to remove a bit of the magic. Also, format srings concatenation have
been discussed in this list some time ago ... (I remembered that safe
format concatenation is possible if we add of an extra fourth type
parameter to format strings ...)

Pierre Weis

INRIA, Projet Cristal, Pierre.Weis@inria.fr, http://cristal.inria.fr/~weis/




^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: AW: Format.sprintf and "%a"
  2000-11-23 15:13 AW: Format.sprintf and "%a" jim.rauser
  2000-11-25 15:43 ` Pierre Weis
@ 2000-11-28 12:52 ` thierry BRAVIER
  1 sibling, 0 replies; 3+ messages in thread
From: thierry BRAVIER @ 2000-11-28 12:52 UTC (permalink / raw)
  To: jim.rauser, caml-list, Pierre.Weis

Here is a small contribution to the printf and format chat between Pierre Weis
and Jim Rauser.

Listed files contain a simple extensible solution to pretty-print objects in
the toplevel
by getting out_channel and string printers from formatter printers.

To get a printable object it is only necessary to inherit from
Pretty.printable_base
and over-ride method pprint : formatter -> unit.

Then if x : foo implements printable, f#sprint : string for instance.

Hope it helps.

Thierry Bravier

pretty.mli and pretty.ml follow ...

(* =========================================================================
 *        File: pretty.mli
 *      Author: Thierry BRAVIER
 *  Time-stamp: <00/11/27 16:06:58 tb>
 * ====================================================================== *)

(* ====================================================================== *)
(* ocaml printf "%a" pretty printers types *)
(* ---------------------------------------------------------------------- *)
type 'a pprinter = Format.formatter -> 'a -> unit
type 'a fprinter = out_channel      -> 'a -> unit
type 'a sprinter = unit             -> 'a -> string

(* ====================================================================== *)
(* pprint to any output device *)
(* ---------------------------------------------------------------------- *)
val pprinter_of_pprinter : 'a pprinter -> 'a pprinter   (* pprint and flush *)

val fprinter_of_pprinter : 'a pprinter -> 'a fprinter   (* pprint to channel
*)
val sprinter_of_pprinter : 'a pprinter -> 'a sprinter   (* pprint to string *)

val printer_of_pprinter  : 'a pprinter -> 'a -> unit    (* pprint to stdout *)

val eprinter_of_pprinter : 'a pprinter -> 'a -> unit    (* pprint to stderr *)

(* ====================================================================== *)
(* printable objects interface and stateless abstract base class *)
(* ---------------------------------------------------------------------- *)
class type printable =
  object
    method pprint : Format.formatter -> unit    (* over-ride,SHOULD NOT
FLUSH*)
    method fprint : out_channel      -> unit    (* DO NOT OVER-RIDE *)
    method sprint : string                      (* DO NOT OVER-RIDE *)
  end

(* ---------------------------------------------------------------------- *)
class virtual printable_base : printable

(* ====================================================================== *)
(* "%a" helpers *)
(* ---------------------------------------------------------------------- *)
val pprinter : #printable pprinter      (* useful with Format.fprintf "%a" *)
val fprinter : #printable fprinter      (* useful with Printf.fprintf "%a" *)
val sprinter : #printable sprinter      (* useful with Printf.sprintf "%a" *)

(* ====================================================================== *)
(* autonomous helpers *)
(* ---------------------------------------------------------------------- *)
val pprint : Format.formatter -> #printable -> unit     (* pprint and flush *)

val fprint : out_channel      -> #printable -> unit     (* pprint to channel
*)
val sprint :                     #printable -> string   (* pprint to string *)

val print  : #printable -> unit                         (* pprint to stdout *)

val eprint : #printable -> unit                         (* pprint to stderr *)

(* ====================================================================== *)

(* ======================================================================
 *        File: pretty.ml
 *      Author: Thierry BRAVIER
 *  Time-stamp: <00/11/27 16:12:44 tb>
 * ====================================================================== *)
open Format

(* ====================================================================== *)
type 'a pprinter = Format.formatter -> 'a -> unit
type 'a fprinter = out_channel      -> 'a -> unit
type 'a sprinter = unit             -> 'a -> string

(* ====================================================================== *)
let pprinter_of_pprinter pp f  x =                              (* flush *)
  Format.fprintf f "@[%a@]@?" pp x
let fprinter_of_pprinter pp o  x =                              (* flush *)
  pprinter_of_pprinter pp (formatter_of_out_channel o)x
let sprinter_of_pprinter pp () x =                              (* flush *)
  let b = Buffer.create 16 in
  (pprinter_of_pprinter pp (formatter_of_buffer b) x);
  Buffer.contents b

(* ---------------------------------------------------------------------- *)
let printer_of_pprinter  pp x=pprinter_of_pprinter pp std_formatter x
(*flush*)
let eprinter_of_pprinter pp x=pprinter_of_pprinter pp err_formatter x
(*flush*)

(* ====================================================================== *)
let pprinter f  x = x#pprint f                          (* SHOULD NOT FLUSH *)

let fprinter o  x = fprinter_of_pprinter pprinter o  x  (* flush *)
let sprinter () x = sprinter_of_pprinter pprinter () x  (* flush *)

(* ====================================================================== *)
let pprint   f  x = pprinter_of_pprinter pprinter f x   (* flush *)
let fprint        = fprinter                            (* flush *)
let sprint      x = sprinter () x                       (* flush *)

(* ---------------------------------------------------------------------- *)
let print       x = printer_of_pprinter  pprinter x     (* flush *)
let eprint      x = eprinter_of_pprinter pprinter x     (* flush *)

(* ====================================================================== *)
class type printable = object
  method pprint : formatter   -> unit
  method fprint : out_channel -> unit
  method sprint : string
end

(* ---------------------------------------------------------------------- *)
class virtual printable_base = object (self)
  method pprint f = Format.fprintf f "printable"        (* SHOULD NOT FLUSH *)

  method fprint o = fprint o self
  method sprint   = sprint   self
end

(* ====================================================================== *)
(* Pretty demo in ocaml toplevel: *)
(* ---------------------------------------------------------------------- *)
(*
#load "pretty.cmo";;                    (* or use ocamlmktop / .ocamlinit *)
#install_printer Pretty.print;;         (* for all #Pretty.printable objects
*)

class foo i j = object
  val mutable x = i
  val y = j
  method incr = x <- x + 1

  (* could be selectively compiled or removed with camlp4 or cpp macros *)
  inherit Pretty.printable_base
  method pprint f =
    Format.fprintf f "@[<hv 1>{foo:@ @[x = %d,@]@ @[y = \"%s\"@]}@]" x y
end;;
let trace x = Format.eprintf "TRACE: @[x = %a@]@\n@?" Pretty.pprinter x;;
let demo x = trace x; x#incr; trace x;;
let x = new foo 123456789 "tagada voila les daltons";;
(* val x : foo = {foo: x = 123456789, y = "tagada voila les daltons"} *)
demo x;;
(*
TRACE: x = {foo: x = 123456789, y = "tagada voila les daltons"}
TRACE: x = {foo: x = 123456790, y = "tagada voila les daltons"}
- : unit = ()
*)
*)
(* ====================================================================== *)

Pierre Weis answered to

> Jim Rauser mailto:rauser@qcentic.de
>
> Have a look at the FAQ:
> http://pauillac.inria.fr/caml/FAQ/FAQ_EXPERT-eng.html
>
> #fsprintf;;
> - : ((Format.formatter -> string -> unit) -> string -> unit,
>      Format.formatter, unit)
>     format -> (Format.formatter -> string -> unit) -> string -> unit
> = <fun>
>
> > What I don't understand is, why it *does* work with Format.fprintf,
> > that is, how the compiler convinces itself that an argument list
> > like ["%a" fmt_string "foo"] unifies with the type [('a,
> > Format.formatter, unit) format]?  I don't recall seeing anything
> > else in the manual that talks about functions with variable arity;
> > is there some extra-linguistic magic going on here?
>
> Yes. Here are some hints about this magic.
>
> First of all, the typechecker has an extra rule for strings: the
> auxiliary rule states that if the typechecking context needs to
> consider a string constant as a value of type ('a, 'b, 'c) format then
> this is not considered as an error.  Furthermore the typechecker
> analyses the contents of the constant format strings to find out what
> can be the types of the arguments of printf. For instance:
>
> # ("" : ('a, 'b, 'c) format);;
> - : ('a, 'b, 'a) format = <abstr>
>
> # let fmt = ("print an int: %i" : ('a, 'b, 'c) format);;
> val fmt : (int -> 'a, 'b, 'a) format = <abstr>
>
> In case of an application of printf the typechecker unifies the result
> type of the application to the one specified by the given format
> string (unit in the case of format for printf, string in the case of
> format for sprintf).
>
> # let f i = Printf.fprintf stdout fmt i;;
> val f : int -> unit = <fun>
> # f 3;;
> print an int: 3- : unit = ()
>
> You have to be careful with partial applications of printf-like
> functions to format strings, since those applications are really
> partial applications! (Meaning that side-effects may happen at each
> new argument passed to the function)!
>
> For instance, the format "print an int: %i", is in some sense
> equivalent to the functional expression:
>
>  print_string "print an int: "; function i -> print_int i
>
> Hence the following behaviours:
>
> # Printf.fprintf stdout fmt;;
> print an int: - : int -> unit = <fun>
>
> and also:
>
> # let f = Printf.fprintf stdout fmt;;
> print an int: val f : int -> unit = <fun>
>
> More generally, a ('a, 'b, 'c) format string value applied to a printf
> like function means that:
>
> -- the resulting function will accept arguments with type 'a
> -- embedded %a applications will accept values of type 'b as first
>    argument
> -- the resulting function will return values of type 'c
>
> # Printf.sprintf;;
> - : ('a, unit, string) format -> 'a = <fun>
>
> # Printf.sprintf "%a";;
> - : (unit -> '_a -> string) -> '_a -> string = <fun>
>
> # Printf.fprintf;;
> - : out_channel -> ('a, out_channel, unit) format -> 'a = <fun>
>
> # Printf.fprintf stdout "%a";;
> - : (out_channel -> '_a -> unit) -> '_a -> unit = <fun>
>
> As you can see, embedded function calls must have the same 'b and 'c
> parameters as the format they are called from (meaning that since
> Printf.fprintf stdout takes a ('a, out_channel, unit) format as format
> string, it can only accept %a convertion specifications with functions
> f having type out_channel -> 'a -> unit).
>
> PS: May be having format strings as lexical entities per se may help
> to remove a bit of the magic. Also, format srings concatenation have
> been discussed in this list some time ago ... (I remembered that safe
> format concatenation is possible if we add of an extra fourth type
> parameter to format strings ...)
>
> Pierre Weis

--
Thierry Bravier                      Dassault Aviation - DGT / DPR / ESA
78, Quai Marcel Dassault              F-92214 Saint-Cloud Cedex - France
Telephone : (33) 01 47 11 53 07          Telecopie : (33) 01 47 11 57 23
E-Mail :                     mailto:thierry.bravier@dassault-aviation.fr




^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2000-11-28 21:26 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2000-11-23 15:13 AW: Format.sprintf and "%a" jim.rauser
2000-11-25 15:43 ` Pierre Weis
2000-11-28 12:52 ` thierry BRAVIER

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).