From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.1.3 (2006-06-01) on yquem.inria.fr X-Spam-Level: * X-Spam-Status: No, score=1.0 required=5.0 tests=AWL,SPF_FAIL autolearn=disabled version=3.1.3 X-Original-To: caml-list@yquem.inria.fr Delivered-To: caml-list@yquem.inria.fr Received: from discorde.inria.fr (discorde.inria.fr [192.93.2.38]) by yquem.inria.fr (Postfix) with ESMTP id AD005BC69 for ; Fri, 24 Aug 2007 03:18:20 +0200 (CEST) Received: from ciao.gmane.org (main.gmane.org [80.91.229.2]) by discorde.inria.fr (8.13.6/8.13.6) with ESMTP id l7O1IJ5d006062 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NO) for ; Fri, 24 Aug 2007 03:18:20 +0200 Received: from list by ciao.gmane.org with local (Exim 4.43) id 1IONoG-0004YR-Nr for caml-list@inria.fr; Fri, 24 Aug 2007 03:18:00 +0200 Received: from ivr94-8-88-162-26-239.fbx.proxad.net ([88.162.26.239]) by main.gmane.org with esmtp (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Fri, 24 Aug 2007 03:18:00 +0200 Received: from li by ivr94-8-88-162-26-239.fbx.proxad.net with local (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Fri, 24 Aug 2007 03:18:00 +0200 X-Injected-Via-Gmane: http://gmane.org/ To: caml-list@inria.fr From: Zheng Li Subject: [ANN] Vprint: a runtime value printer module Date: Fri, 24 Aug 2007 03:19:18 +0200 Message-ID: <87d4xdohk9.fsf@pps.jussieu.fr> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Complaints-To: usenet@sea.gmane.org X-Gmane-NNTP-Posting-Host: ivr94-8-88-162-26-239.fbx.proxad.net User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.1.50 (gnu/linux) Cancel-Lock: sha1:prXh3BVPmjIftCdJ3XLFzVYlxPo= Sender: news X-Miltered: at discorde with ID 46CE31DB.001 by Joe's j-chkmail (http://j-chkmail . ensmp . fr)! X-Spam: no; 0.00; runtime:01 runtime:01 ocaml:01 ocaml:01 variants:01 toplevel:01 val:01 val:01 inference:01 inference:01 ocaml's:01 toplevel:01 cmo:01 cmx:01 camlp:01 Hi, In the spirit of "release early, release often", we therefore announce the first release of Vprint, a runtime value printer module for OCaml. Be warned, the module is extremely experimental at the moment, and the implementation is very hasty (I got the main idea yesterday when finalising my GSoc project and now it's here). So be ready to encounter stupid errors and even core dump, but do remember to send me a message about that so I can improve. Here is the introduction from README: DESCRIPTION =========== Vprint is a value printer module for OCaml. It prints any value at running time with a simple generic printer. It can be used for the following purposes: help debugging, inspect data representation, test type casting, runtime dispatch based on type representations and avoid writing pretty printers. - Help debugging. For rapid development reason, we don't want to write a printer/formatter function for each data type. Moreover, even if we've got the printers for each type, specifying printers to inspect the running time values, which are usually the arbitrary combinations (tuple, list, variants, objects etc.) of them, is still tedious. - Inspect data representation The module can shows you all kinds of data (of different types) that having the same memory layout as a input data. - Test type casting By printing a value with the format of another type, it can help to test whether a data can be safely cast to another type without actually doing it. But be warned, the real casting can still fail even if the printing successes. - Runtime dispatch based on type representations In the same spirit as above. - Avoid writing pretty printers (TODO) Without considering debugging, sometimes you still need to output the values of some data type. If you don't care much about the format and just want the result shew, then you probably don't have to write it by your own now; if the OCaml toplevel printer is something you'd like, we're on the way there, if given more time. FEATURES ======== There are just two main functions "print" and "print_all" (and their output-to-string version "sprint" and "sprint_all") val print: ?fmt:fmt -> 'a -> unit val print_all: ?fmt:fmt -> 'a -> unit The "print" function can prints any data with a generic printer. However, it's well-known fact that OCaml runtime doesn't carry type information, so the output maybe too generic to read sometimes, e.g. the representation of list is something like a linked pairs. The implementation tries to inference the actual data type, however due to the theoretical limitation, there do exist many cases where the output is not satisfactory such as the list/pair example. And we don't believe it's appropriate to just choose a probable result based on probabilities, because misleading is even worse. (1, (2, (3, (4, (5, (6, 0)))))) is mostly likely to be a list, but not necessarily. In such cases, you can give more hints to the system by using the optional ~fmt parameters. The format of fmt is very close to type representation and supports combination, so it's quite easy to write (check example section). Besides it accepts the default arguments "__" (means "anything") and try its best to inference more information based on the less information being given, so you don't have to write every details. In such as sense, fmt can be used as a simpler formatter if you want to pretty print some values as output but is lazy to write complex formatter, just gives more specific fmt information if you want better effect. For now, we use rather simple string output function, but we'll definitely move to Format and build better output a la OCaml's toplevel output. You can also use "print" to do preliminary tests of type casting. By printing value x of type t with a fmt associated with type t', you get more confidence to cast x to type t', instead of making real casting and get core dump, here you only get a Fmt_Error exception. I believe you've been warned it's not definitive, but the point is that the function can help you to rule out a lot possibilities beforehand. In the same spirit, you can use the function to do runtime dispatch based on type representations (not types! it's like representation level duck typing). E.g, something like (not tested yet) open Vprint let o = input_value ch in try ignore (sprint ~fmt:_s o); do_sth_to_o_as_string with Fmt_Error _ -> try ignore (sprint ~fmt:_f o); do_sth_to_o_as_float with Fmt_Error _ -> try ignore (sprint ~fmt:_l_ o); do_sth_to_o_as_list with Fmt_Error _ -> ...... Finally, the "print_all" function can help you to inspect the representation of data, especially it lists all the data (of any types) that having the same memory representation as a value. For example, when you print a value with the default formatter: "print v", the output is so generic that won't satisfy you. Then you may wonder why the output is so generic? You can get the answer with "print_all v", then you'll see all kinds of values which having the same memory representation as your input "v", that's why the system won't be able to decide which one of them is the "v" hence print less meaningful result. INSTALL ======= It's a simple module, just compile the source to cmo/cmx to use. The source itself makes use of camlp4 (just for stream function), so compile it with option "-pp camlp4o". The result modules doesn't depend on camlp4, so you don't have to specify the camlp4 option when you make use of vprint. We also provide Makefile script with the following command: make all, make install, make doc, make clean, make uninstall. Only tested under the following environment: Linux, OCaml 3.10.0, GNU Make 3.81. COPYRIGHT ========= See file ./LICENCE EXAMPLE ======= # open Vprint (* Some values are easy to identify *) # print ("asdf", [||], 3.14, [|1.01; 2.689|], 32l, 64L, 111n);; <"asdf", [||], 3.14, [|1.01; 2.689|], 32l, 64L, 111n> (* Some are difficult *) # print [1; 2; 3; 4];; <1', <2', <3', <4', 0'>>>> (* By giving it some type information, it's much better. "_l __" means list of 'any, or use _l_ instead *) # print ~fmt:(_l __) [1; 2; 3; 4];; [1'; 2'; 3'; 4'] (* Why the number has a ' after it? *) # print 0;; 0' (* Because there are several datum having this same representations we don't know who is this guy currently *) # print_all 0;; [] (* it could be empty list *) () (* or the unit *) '^@' (* or the char '^@" *) Con0 (* or the 1st no-param constructor of some variant type *) `Var0 (* or a no-param polymorphic variants with interval id = 1 *) 0 (* or, at last, the int value 0! *) (* print_all can also take ~fmt arguments to restrict the choices, now the ' is gone since 0 is the only choice as a int "_i" *) # print_all ~fmt:_i 0;; 0 (* Like the "'", "< >" is sequence whose type is yet to decide *) # print (3, 4);; <3', 4'> - : unit = () # print_all (3, 4);; [|'^C'; '^D'|] (* array of 2 char, first is '^C", then '^D' *) {'^C'; '^D'} (* record of 2 fields, both char *) ('^C', '^D') (* tuple of 2 char *) Con0# ('^C', '^D') (* it's a variant type, here the value is its 1st with-param constructors with 2 char as params *) ('^C', Con4) (* tuple of char and variant whose value here is its 5th no-param constructor *) ...... `Var3 4 (* a polymorphic variant type, its value here is a constructor with id=3 and taking int 4 as param *) [|3; 4|] (* int array of two int *) ...... (3, 4) (* int tuple, our current input! *) ...... (* 50+ possibilities in total *) (* Really ? test *) # type t = A of char * char | B | C of t list;; # print (A ('\003', '\004'), [|3; 4|]);; <<3', 4'>, <3', 4'>> (* Make use of ~fmt, check manual for details, be aware about the 'any: "_" "__" *) (* Print polymorphic variants *) # print ~fmt:(_l _'v_) [`How "vvv"; `Are 3.333; `You];; [`How "vvv"; `Are 3.333; `You] (* Print functions, code * environment *) # let k = 5;; # let rec f x = g x + k and g x = f x - k;; # print f; print g;; (* Print object, id * code * environment *) # print (object val x = 10 val y = 15 method get_xy = x + y end);; (* Print forced lazy value *) # let l = lazy (8.28, "vvv") in let _ = Lazy.force l in print l;; lazy <8.28, "vvv"> (* fmt = 'any * int, *~ is the AND combinator, for collection *) # print ~fmt:(__ *~ _i) (3.14, 3);; <3.14, 3> (* fmt = 'v list and 'v = C' of char * char | C'' of 'any list The // is OR combinator, for variants and polymorphic variants. You can rewrite the variants part as _v (_c *~ _c) // _v _l_ *) # print ~fmt: (_l (_v (_c *~ _c // _l_))) [A ('x', 'y'); C []];; [Con0# ('x', 'y'); Con1# []] (* fmt = int * 'any list * 'b array and 'b = 'any variant *) # print ~fmt:(_i *~ _l_ *~ _a _v_) (3, [3.14; 9.9], [|B; B|]);; <3, [3.14; 9.9], [|Con0; Con0|]> (* fmt = int array list list, the use of combinator @. (read as "OF" as in "list of list of int array) can reduce "..))))..", otherwise we should write it as (_l (_l (_a _i))) *) # print ~fmt:(_l @._l @._a _i) [[[|1; 2|]; [|3; 4|]]];; [[[|1; 2|]; [|3; 4|]]] (* Anyway, it's really hard to describe all the details here, you'll have to read the manual and play with it by yourself to understand. *) Cheers -- Zheng Li http://www.pps.jussieu.fr/~li