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.5 required=5.0 tests=AWL,DATE_IN_PAST_12_24 autolearn=disabled version=3.1.3 X-Original-To: caml-list@yquem.inria.fr Delivered-To: caml-list@yquem.inria.fr Received: from mail4-relais-sop.national.inria.fr (mail4-relais-sop.national.inria.fr [192.134.164.105]) by yquem.inria.fr (Postfix) with ESMTP id CF6FABC69 for ; Wed, 17 Oct 2007 08:51:45 +0200 (CEST) X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: Ah4FACpOFUfUnw7Ug2dsb2JhbACCOIwVAgEIBAYREQc X-IronPort-AV: E=Sophos;i="4.21,287,1188770400"; d="scan'208";a="18123063" Received: from ptb-relay01.plus.net ([212.159.14.212]) by mail4-smtp-sop.national.inria.fr with ESMTP; 17 Oct 2007 08:51:45 +0200 Received: from [80.229.56.224] (helo=beast.local) by ptb-relay01.plus.net with esmtp (Exim) id 1Ii2kq-00043y-Fc for caml-list@yquem.inria.fr; Wed, 17 Oct 2007 07:51:44 +0100 From: Jon Harrop Organization: Flying Frog Consultancy Ltd. To: caml-list@yquem.inria.fr Subject: Re: [Caml-list] Pattern-matching destructors ? Date: Tue, 16 Oct 2007 18:28:04 +0100 User-Agent: KMail/1.9.7 References: <1192548046.6061.18.camel@Blefuscu> In-Reply-To: <1192548046.6061.18.camel@Blefuscu> MIME-Version: 1.0 Content-Type: text/plain; charset="iso-8859-15" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline Message-Id: <200710161828.04519.jon@ffconsultancy.com> X-Spam: no; 0.00; node:01 constructors:01 ocaml:01 ocaml:01 camlp:01 ocaml's:01 overkill:01 node:01 expr:01 unop:01 expr:01 binop:01 val:01 unop:01 binop:01 On Tuesday 16 October 2007 16:20:45 David Teller wrote: > I'm currently working on static analysis of JavaScript 2. For this, I > need to keep lots of informations in each node of my AST, including > things such as line/column (for better error messages), unique > identifier (for storing inferred information), etc. As things progress, > I fear that the number of such informations is growing prohibitive and > very much getting into the way of pattern-matching. > > However, I do remember reading on this list a discussion regarding the > possibility of extending pattern-matching so as to allow matching > against user-defined criteria rather than constructors -- I'm afraid I > don't remember the name of these criteria, possibly "destructors" or > "queries". > > As far as I remember this discussion, the feature was present in F# (or > at least planned), but not in OCaml. Could anyone tell me if things have > evolved on the OCaml side, perhaps with a little camlp4 magic ? You are referring to the pattern matching extension called "views" that is= =20 implemented in F# and called "active patterns" there. The primary use of=20 active patterns in F# is to provide a view of an existing data structure th= at=20 looks like a variant type and can be destructured using pattern matching. F= or=20 example, to view a .NET XML DOM tree (a tree of objects) as the simple=20 variant type used by OCaml's excellent xml-light library. That is certainly a great language feature and is all the more important in= a=20 language that wants to interrogate non-native (i.e. non-F#) APIs like=20 the .NET libraries. However, this feature is overkill for your situation=20 because you have complete control over the data structure used. As Jeff has= =20 already said: you want to bundle your metadata into a record so that you ca= n=20 choose whether or not to dissect it in the pattern match. =46rom what you have said, you might want to abstract your metadata into a= =20 record: type 'node meta =3D {loc: int; char: int; id: int; node: 'node} Your AST might become: type expr_aux =3D | Int of int | Var of string | UnOp of [`Neg] * expr | BinOp of [`Add | `Sub | `Mul | `Div] * expr * expr and expr =3D {loc: int; char: int; id: int; node: expr_aux};; And your functions become: # let loc_of e =3D e.loc;; val loc_of : expr -> int =3D # let rec eval assoc e =3D match e.node with | Int n -> n | Var v -> (try assoc v with Not_found -> invalid_arg (string_of_int e.loc))=20 | UnOp(`Neg, f) -> -eval assoc f | BinOp(op, f, g) -> let f =3D eval assoc f and g =3D eval assoc g in match op with | `Add -> f + g | `Sub -> f - g | `Mul -> f * g | `Div -> f / g;; val eval : (string -> int) -> expr -> int =3D You might use it like this: # let assoc =3D function _ -> raise Not_found;; val assoc : 'a -> 'b =3D # let mk e =3D {loc=3D0; char=3D0; id=3D0; node=3De};; val mk : expr_aux -> expr =3D # eval assoc (mk(BinOp(`Add, mk(Int 3), mk(Int 4))));; =2D : int =3D 7 Interestingly, I recently discovered that case classes are Scala's answer t= o=20 this problem. IMHO, this ordinary ML answer is just fine. This topic and many others are described in detail with working examples in= =20 the OCaml Journal. =2D-=20 Dr Jon D Harrop, Flying Frog Consultancy Ltd. http://www.ffconsultancy.com/products/?e