caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Jon Harrop <jon@ffconsultancy.com>
To: "Undisclosed.Recipients": ;
Cc: caml-list@yquem.inria.fr
Subject: Re: [Caml-list] Patterns that evaluate
Date: Wed, 14 Feb 2007 21:05:48 +0000	[thread overview]
Message-ID: <200702142105.48663.jon@ffconsultancy.com> (raw)
In-Reply-To: <1171479313.24335.33.camel@localhost.localdomain>

On Wednesday 14 February 2007 18:55, Gerd Stolpmann wrote:
> You are a bit quick. Before discussing syntax it is more important to
> define the semantics of such patterns. I mean we have already three
> predefined kinds of equality in O'Caml:
>
> - ( == )
> - ( = )
> - (fun x y -> compare x y = 0)
>
> I admit I do not prefer any one of them. So which equality should be
> used to test whether the variable is equal to the matched part of the
> value?

None, I think.

Pattern matching is clearly an awesome tool, and there are many ways that 
OCaml's pattern matching can be improved upon. To me, active patterns are 
about applying user-defined functions to test partial matches. So I would 
want the user to explicitly supply the appropriate comparison function, as 
you do with Set and Map.

There are many places where this is advantageous. My favourite example is term 
rewriting.

Assuming the existence of +: and *: operators to add and multiply symbolic 
expressions, we can currently write a symbolic derivative function:

let rec d f x = match f with
  | Var y when x=y -> Int 1
  | Int _ | Var _ -> Int 0
  | Add(f, g) -> d f x +: d g x
  | Mul(f, g) -> f *: d g x +: g *: d f x

That is already nicer than most other languages but there is plenty of room 
for improvement.

Before we even get to active patterns, we can add operator overloading to use 
+ and * instead:

let rec d f x = match f with
  | Var y when x=y -> Int 1
  | Int _ | Var _ -> Int 0
  | Add(f, g) -> d f x + d g x
  | Mul(f, g) -> f * d g x + g * d f x

That's a bit better. Why not have infix operators as constructors and 
deconstructors:

let rec d f x = match f with
  | Var y when x=y -> Int 1
  | Int _ | Var _ -> Int 0
  | f + g -> d f x + d g x
  | f * g -> f * d g x + g * d f x

But we often want more than that. Consider a function that naively performs 
the substitution a+b -> c taking commutativity into account:

let rec mem x = function
  | Int _ | Var _ as f -> f=x
  | Add(f, g) | Mul(f, g) as e -> mem_rec e x f || mem_rec e x g
and mem_rec e x f = match e, f with
  | Add _, Add _ | Mul _, Mul _ -> mem x f
  | Add _, _ | Mul _, _ -> false
  | _ -> assert false

let rec sub = function
  | Int _ | Var _ as f -> f
  | Add _ as e when mem (Var "a") e && mem (Var "b") e ->
      Var "c" +: sub(e -: Var "a" -: Var "b")
  | Add(f, g) -> Add(sub f, sub g)
  | Mul(f, g) -> Mul(sub f, sub g)

That could be written much more elegantly, something like this:

let rec rewrite rule = function
  | Int _ | Var _ as f -> rule f
  | Add(f, g) -> Add(rewrite rule f, rewrite rule g)
  | Mul(f, g) -> Mul(rewrite rule f, rewrite rule g)

let sub = function
  | Var "a" + Var "b" + t -> Var "c" + t
  | expr -> expr

let sub = rewrite sub

where the pattern Var "a" + Var "b" + t searches the symbolic expression 
for "a" and "b" and puts the remainder in t. So the "+" is an active pattern 
that takes two patterns as arguments and matches if it can split an 
expression into an addition of the two patterns. This active pattern can then 
be made arbitrarily clever with respect to how it slices and dices the data 
structure. Better yet, the code is no longer tied to the underlying 
representation of terms, so the inefficient representation of a sum as a list 
of Add cells can be replaced by a mapping from terms to their multiples, 
which can then be searched in O(log n) time instead of O(n) time.

Given that these kinds of ideas have been around for so long (that Haskell 
views paper), I'm surprised that people have implemented active patterns in 
more languages.

-- 
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
OCaml for Scientists
http://www.ffconsultancy.com/products/ocaml_for_scientists


  parent reply	other threads:[~2007-02-14 21:12 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-02-13 22:04 Jacques Carette
2007-02-13 22:07 ` [Caml-list] " Jon Harrop
2007-02-14  0:10   ` Jacques Carette
2007-02-14 18:20   ` Edgar Friendly
2007-02-14 18:55     ` Gerd Stolpmann
2007-02-14 19:10       ` Denis Bueno
2007-02-14 19:11       ` Jacques Carette
2007-02-14 19:25         ` Gerd Stolpmann
2007-02-14 20:30           ` Edgar Friendly
2007-02-14 21:05       ` Jon Harrop [this message]
2007-02-14 21:33         ` Jacques Carette
2007-02-14 22:34   ` Martin Jambon
2007-02-15  0:26     ` Jacques Garrigue
2007-02-15  3:57       ` Jon Harrop
2007-02-15 22:43         ` Don Syme
2007-02-14 20:29 ` Nathaniel Gray
2007-02-14 21:10   ` Jacques Carette
2007-02-15  3:53     ` skaller
2007-02-15 13:41       ` Jacques Carette
2007-02-15 14:10         ` skaller
2007-02-15 20:43     ` Nathaniel Gray
2007-03-07 11:15       ` Oliver Bandel

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=200702142105.48663.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).