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=0.0 required=5.0 tests=AWL autolearn=disabled version=3.1.3 Received: from concorde.inria.fr (concorde.inria.fr [192.93.2.39]) by yquem.inria.fr (Postfix) with ESMTP id E5437BB83 for ; Fri, 1 Sep 2006 21:30:19 +0200 (CEST) Received: from smtp3.adl2.internode.on.net (smtp3.adl2.internode.on.net [203.16.214.203]) by concorde.inria.fr (8.13.6/8.13.6) with ESMTP id k81JUBJ8015261 for ; Fri, 1 Sep 2006 21:30:19 +0200 Received: from rosella (ppp14-47.lns2.syd7.internode.on.net [59.167.14.47]) by smtp3.adl2.internode.on.net (8.13.6/8.13.5) with ESMTP id k81JTsYl039541; Sat, 2 Sep 2006 04:59:55 +0930 (CST) (envelope-from skaller@users.sourceforge.net) Subject: Re: [Caml-list] Polymorphic variants question From: skaller To: David Allsopp Cc: OCaml List In-Reply-To: <012901c6cdec$64edf490$6a7ba8c0@treble> References: <012901c6cdec$64edf490$6a7ba8c0@treble> Content-Type: text/plain Date: Sat, 02 Sep 2006 05:29:53 +1000 Message-Id: <1157138993.22787.34.camel@rosella.wigram> Mime-Version: 1.0 X-Mailer: Evolution 2.6.1 Content-Transfer-Encoding: 7bit X-Miltered: at concorde with ID 44F88A43.001 by Joe's j-chkmail (http://j-chkmail.ensmp.fr)! X-Spam: no; 0.00; variants:01 bool:01 variants:01 mli:01 endline:01 endline:01 inference:01 scattered:01 incorrectly:01 annotations:01 ocaml's:01 compiler:01 overloading:01 dfns:01 regexp:01 On Fri, 2006-09-01 at 18:31 +0100, David Allsopp wrote: > let f x = if x = `A then (true, `B) else (false, x) > let (f : [`A | `C] -> bool * [`A | `B | `C]) = fun x -> ... BTW: when using polymorphic variants I find it is a good idea to: (a) provide names (aliases, abbreviations) for your types. (b) annotate function arguments and returns -- if not all of them, focus on the top level ones: it's necessary for the mli file anyhow. (c) Prefer let f x = match x with | ... over let f = fun x -> ... and let f = function | .. and in particular for big top level functions like let f (x:t1):t2 = print_endline ("In Debug " ^ string_of_t1 x); let r : t2 = match x with .. in print_endline ("Out Debug " ^ string_of_t2 r); r This shape instruments the input type, output type, input value and output value. The thing about polymorphic variants is that because (a) the typing is structural not nominal (like ordinary variants) (b) type inference tries to figure out the types from usage scattered through the program it is not only easy to extend them .. it is easy to extend them incorrectly .. and the diagnostics from big variants are horrible :) Using annotations tends to localise the errors and give you more information, including Ocaml's smart trick of using your own alias. (This is very clever :) It also often reports that you're missing a particular constructor. I've been using PM variants for a while now. I just converted a couple of non-PM ones over. I kind of like this function, it prints many of the types I use in my compiler in one function, almost like overloading :) let string_of_term dfns term = match term with | #qualified_name_t as x -> string_of_qualified_name x | #regexp_t as x -> string_of_re x | #typecode_t as x -> string_of_typecode x | #tpattern_t as x -> string_of_tpattern x | #literal_t as x -> string_of_literal x | #expr_t as x -> string_of_expr x | #pattern_t as x -> string_of_pattern x | #statement_t as x -> string_of_statement 0 x | #exe_t as x -> string_of_exe 0 x | #btypecode_t as x -> string_of_btypecode dfns x (* hack .. the type because tbexpr_t is a pair not a variant *) | #bexpr_t as x -> string_of_bound_expression dfns (x,`BTYP_void) | #bexe_t as x -> string_of_bexe dfns 0 x | #ast_term_t as x -> string_of_ast_term 0 x (* hack cause we don't know the name *) | #symbol_definition_t as x -> string_of_symdef x "unk" [] | #bbdcl_t as x -> string_of_bbdcl dfns x 0 | #param_kind_t as x -> string_of_param_kind x | #property_t as x -> string_of_property x | #c_t as x -> string_of_code_spec x | #dcl_t as x -> string_of_dcl 0 "unk" (Some 0) [] x | #asm_t as x -> string_of_asm 0 x | #iface_t as x -> string_of_iface 0 x | #access_t as x -> string_of_access x | #biface_t as x -> string_of_biface dfns 0 x | #btype_qual_t as x -> string_of_bqual dfns x | #type_qual_t as x -> string_of_qual x | #requirement_t as x -> string_of_raw_req x | #ikind_t as x -> string_of_ikind x | #named_req_expr_t as x -> string_of_named_req_expr x | #raw_req_expr_t as x -> string_of_raw_req_expr x | #glr_term_t as x -> string_of_glr_term x let st dfns term = string_of_term dfns term This function doesn't DO anything I couldn't already do. It just saves me worrying what the type of the term is, and then trying to remember the name of the function that prints it. The PM variants let me unify any variants types I'm using, which is why I converted most of the remaining non-PM variants over. Stick with them .. they're worth it! -- John Skaller Felix, successor to C++: http://felix.sf.net