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 discorde.inria.fr (discorde.inria.fr [192.93.2.38]) by yquem.inria.fr (Postfix) with ESMTP id 8C116BC0A for ; Wed, 14 Feb 2007 22:12:26 +0100 (CET) Received: from pih-relay04.plus.net (pih-relay04.plus.net [212.159.14.131]) by discorde.inria.fr (8.13.6/8.13.6) with ESMTP id l1ELCPSg013531 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NO) for ; Wed, 14 Feb 2007 22:12:26 +0100 Received: from [80.229.56.224] (helo=beast.local) by pih-relay04.plus.net with esmtp (Exim) id 1HHRQP-0000An-31 for caml-list@yquem.inria.fr; Wed, 14 Feb 2007 21:12:25 +0000 From: Jon Harrop Organization: Flying Frog Consultancy Ltd. Subject: Re: [Caml-list] Patterns that evaluate Date: Wed, 14 Feb 2007 21:05:48 +0000 User-Agent: KMail/1.9.5 Cc: caml-list@yquem.inria.fr References: <45D23608.4030104@mcmaster.ca> <45D352F2.3080003@gmail.com> <1171479313.24335.33.camel@localhost.localdomain> In-Reply-To: <1171479313.24335.33.camel@localhost.localdomain> MIME-Version: 1.0 Content-Type: text/plain; charset="iso-8859-15" Content-Transfer-Encoding: 7bit Content-Disposition: inline To: "Undisclosed.Recipients": ; Message-Id: <200702142105.48663.jon@ffconsultancy.com> X-Miltered: at discorde with ID 45D37B39.002 by Joe's j-chkmail (http://j-chkmail . ensmp . fr)! X-Spam: no; 0.00; gerd:01 stolpmann:01 syntax:01 semantics:01 o'caml:01 ocaml's:01 rewriting:01 derivative:01 overloading:01 infix:01 constructors:01 elegantly:01 expr:01 expr:01 arbitrarily:01 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