caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Jon Harrop <jon@ffconsultancy.com>
To: caml-list@yquem.inria.fr
Subject: Re: [Caml-list] Pattern-matching destructors ?
Date: Tue, 16 Oct 2007 18:28:04 +0100	[thread overview]
Message-ID: <200710161828.04519.jon@ffconsultancy.com> (raw)
In-Reply-To: <1192548046.6061.18.camel@Blefuscu>

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 
implemented in F# and called "active patterns" there. The primary use of 
active patterns in F# is to provide a view of an existing data structure that 
looks like a variant type and can be destructured using pattern matching. For 
example, to view a .NET XML DOM tree (a tree of objects) as the simple 
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 
language that wants to interrogate non-native (i.e. non-F#) APIs like 
the .NET libraries. However, this feature is overkill for your situation 
because you have complete control over the data structure used. As Jeff has 
already said: you want to bundle your metadata into a record so that you can 
choose whether or not to dissect it in the pattern match.

From what you have said, you might want to abstract your metadata into a 
record:

  type 'node meta = {loc: int; char: int; id: int; node: 'node}

Your AST might become:

  type expr_aux =
    | Int of int
    | Var of string
    | UnOp of [`Neg] * expr
    | BinOp of [`Add | `Sub | `Mul | `Div] * expr * expr
  and expr = {loc: int; char: int; id: int; node: expr_aux};;

And your functions become:

# let loc_of e = e.loc;;
val loc_of : expr -> int = <fun>

# let rec eval assoc e =
    match e.node with
    | Int n -> n
    | Var v ->
        (try assoc v with Not_found -> invalid_arg (string_of_int e.loc)) 
    | UnOp(`Neg, f) -> -eval assoc f
    | BinOp(op, f, g) ->
        let f = eval assoc f and g = 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 = <fun>

You might use it like this:

# let assoc = function _ -> raise Not_found;;
val assoc : 'a -> 'b = <fun>
# let mk e = {loc=0; char=0; id=0; node=e};;
val mk : expr_aux -> expr = <fun>
# eval assoc (mk(BinOp(`Add, mk(Int 3), mk(Int 4))));;
- : int = 7

Interestingly, I recently discovered that case classes are Scala's answer to 
this problem. IMHO, this ordinary ML answer is just fine.

This topic and many others are described in detail with working examples in 
the OCaml Journal.

-- 
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
http://www.ffconsultancy.com/products/?e


  parent reply	other threads:[~2007-10-17  6:51 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-10-16 15:20 David Teller
2007-10-16 17:09 ` [Caml-list] " Alain Frisch
2007-10-16 17:28 ` Jon Harrop [this message]
2007-10-16 17:44 ` Jean-Christophe
2007-10-16 18:53   ` David Teller

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=200710161828.04519.jon@ffconsultancy.com \
    --to=jon@ffconsultancy.com \
    --cc=caml-list@yquem.inria.fr \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).