From mboxrd@z Thu Jan 1 00:00:00 1970 Received: (from majordomo@localhost) by pauillac.inria.fr (8.7.6/8.7.3) id UAA30140; Fri, 30 Jul 2004 20:26:00 +0200 (MET DST) X-Authentication-Warning: pauillac.inria.fr: majordomo set sender to owner-caml-list@pauillac.inria.fr using -f Received: from concorde.inria.fr (concorde.inria.fr [192.93.2.39]) by pauillac.inria.fr (8.7.6/8.7.3) with ESMTP id UAA31173 for ; Fri, 30 Jul 2004 20:25:59 +0200 (MET DST) X-SPAM-Warning: Sending machine is listed in blackholes.five-ten-sg.com Received: from wetware.com (wetware.wetware.com [199.108.16.1]) by concorde.inria.fr (8.12.10/8.12.10) with ESMTP id i6UIPvSH032561 for ; Fri, 30 Jul 2004 20:25:58 +0200 Received: from [208.177.152.17] (helo=[10.0.1.9]) by wetware.com with esmtp (Exim 4.20) id 1Bqc4q-0005jn-8s; Fri, 30 Jul 2004 11:25:56 -0700 Mime-Version: 1.0 (Apple Message framework v618) In-Reply-To: References: <16646.64470.304530.264731@soggy.deldotd.com> <16647.5177.849829.421587@soggy.deldotd.com> <4107544C.4040502@baretta.com> <410898F0.6030804@baretta.com> <41097D53.5050607@baretta.com> Content-Type: multipart/mixed; boundary=Apple-Mail-9--911876311 Message-Id: From: james woodyatt Subject: [Caml-list] kaplan-okasaki-tarjan deque (was "looping recursion") Date: Fri, 30 Jul 2004 11:25:56 -0700 To: brogoff , Ocaml Trade X-Mailer: Apple Mail (2.618) X-Miltered: at concorde with ID 410A92B5.001 by Joe's j-chkmail (http://j-chkmail.ensmp.fr)! X-Loop: caml-list@inria.fr X-Spam: no; 0.00; woodyatt:01 jhw:01 wetware:01 deque:01 recursion:01 brogoff:01 catenable:01 deques:01 deque:01 judicious:01 recursion:01 annotations:01 bootstrapped:01 haskell:01 invalidates:01 X-Attachments: type="application/octet-stream" name="cf_kotdeque.ml" name="cf_kotdeque.ml" type="application/octet-stream" name="cf_kotdeque.mli" name="cf_kotdeque.mli" Sender: owner-caml-list@pauillac.inria.fr Precedence: bulk --Apple-Mail-9--911876311 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=US-ASCII; format=flowed On 30 Jul 2004, at 10:07, brogoff wrote: > > I would like to see an implementation of the catenable deques only > using simple list ops (not laziness) described by Kaplan and Tarjan, > in OCaml. Sure. Here is the basic implementation I did for performance comparisons. --Apple-Mail-9--911876311 Content-Transfer-Encoding: 7bit Content-Type: application/octet-stream; x-unix-mode=0644; name="cf_kotdeque.ml" Content-Disposition: attachment; filename=cf_kotdeque.ml (*---------------------------------------------------------------------------* IMPLEMENTATION cf_kotdeque.ml Copyright (c) 2003, James H. Woodyatt All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *---------------------------------------------------------------------------*) type 'a buffer = | B1 of 'a | B2 of 'a * 'a | B3 of 'a * 'a * 'a | B4 of 'a * 'a * 'a * 'a | B5 of 'a * 'a * 'a * 'a * 'a | B6 of 'a * 'a * 'a * 'a * 'a * 'a | B7 of 'a * 'a * 'a * 'a * 'a * 'a * 'a | B8 of 'a * 'a * 'a * 'a * 'a * 'a * 'a * 'a let pushBufferL x = function | B1 a -> B2 (x,a) | B2 (a,b) -> B3 (x,a,b) | B3 (a,b,c) -> B4 (x,a,b,c) | B4 (a,b,c,d) -> B5 (x,a,b,c,d) | B5 (a,b,c,d,e) -> B6 (x,a,b,c,d,e) | B6 (a,b,c,d,e,f) -> B7 (x,a,b,c,d,e,f) | B7 (a,b,c,d,e,f,g) -> B8 (x,a,b,c,d,e,f,g) | B8 _ as y -> assert (not true); y let pushBufferR x = function | B1 a -> B2 (a,x) | B2 (a,b) -> B3 (a,b,x) | B3 (a,b,c) -> B4 (a,b,c,x) | B4 (a,b,c,d) -> B5 (a,b,c,d,x) | B5 (a,b,c,d,e) -> B6 (a,b,c,d,e,x) | B6 (a,b,c,d,e,f) -> B7 (a,b,c,d,e,f,x) | B7 (a,b,c,d,e,f,g) -> B8 (a,b,c,d,e,f,g,x) | B8 _ as y -> assert (not true); y let popBufferL = function | B1 _ -> assert false | B2 (x,a) -> x, B1 a | B3 (x,a,b) -> x, B2 (a,b) | B4 (x,a,b,c) -> x, B3 (a,b,c) | B5 (x,a,b,c,d) -> x, B4 (a,b,c,d) | B6 (x,a,b,c,d,e) -> x, B5 (a,b,c,d,e) | B7 (x,a,b,c,d,e,f) -> x, B6 (a,b,c,d,e,f) | B8 (x,a,b,c,d,e,f,g) -> x, B7 (a,b,c,d,e,f,g) let popBufferR = function | B1 _ -> assert false | B2 (a,x) -> x, B1 a | B3 (a,b,x) -> x, B2 (a,b) | B4 (a,b,c,x) -> x, B3 (a,b,c) | B5 (a,b,c,d,x) -> x, B4 (a,b,c,d) | B6 (a,b,c,d,e,x) -> x, B5 (a,b,c,d,e) | B7 (a,b,c,d,e,f,x) -> x, B6 (a,b,c,d,e,f) | B8 (a,b,c,d,e,f,g,x) -> x, B7 (a,b,c,d,e,f,g) type 'a t = | Z | Y of 'a buffer | U of 'a buffer * 'a triple t * ('a * 'a) * 'a triple t * 'a buffer and 'a triple = | T1 of 'a buffer | T2 of 'a buffer * 'a buffer | T3 of 'a buffer * 'a triple t * 'a buffer let popLeftNaive = function | Z -> assert false | Y (B1 x) -> x, Z | Y b -> let x, b = popBufferL b in x, Y b | U (pr, ld, mid, rd, sf) -> let x, pr = popBufferL pr in x, U (pr, ld, mid, rd, sf) let popRightNaive = function | Z -> assert false | Y (B1 x) -> x, Z | Y b -> let x, b = popBufferR b in x, Y b | U (pr, ld, mid, rd, sf) -> let x, sf = popBufferR sf in x, U (pr, ld, mid, rd, sf) let rec pushLeft x = function | Z -> Y (B1 x) | Y (B8 (a,b,c,d,e,f,g,h)) -> U (B4 (x,a,b,c), Z, (d,e), Z, B3 (f,g,h)) | Y b -> Y (pushBufferL x b) | U (pr, ld, mid, rd, sf) as d -> match pr with | B6 _ -> let pr, ld, mid, rd, sf = greenLeft pr ld mid rd sf in U (pushBufferL x pr, ld, mid, rd, sf) | _ -> U (pushBufferL x pr, ld, mid, rd, sf) and pushRight x = function | Z -> Y (B1 x) | Y (B8 (a,b,c,d,e,f,g,h)) -> U (B3 (a,b,c), Z, (d,e), Z, B4 (f,g,h,x)) | Y b -> Y (pushBufferR x b) | U (pr, ld, mid, rd, sf) as d -> match sf with | B6 _ -> let pr, ld, mid, rd, sf = greenRight pr ld mid rd sf in U (pr, ld, mid, rd, pushBufferR x sf) | _ -> U (pr, ld, mid, rd, pushBufferR x sf) and greenLeft pr ld mid rd sf = match pr with | B6 (e1,e2,e3,e4,e5,e6) -> let ld = (Obj.magic pushLeft) (T1 (B2 (e5,e6))) ld in B4 (e1,e2,e3,e4), ld, mid, rd, sf | B3 _ -> begin match ld, rd with | Y _, _ | U _, _ -> let t, l = let t, _ = popLeftNaive ld in match t with | T1 (B3 _) | T2 (B3 _, _) | T3 _ -> popLeftNaive ld | _ -> match (Obj.magic popLeft) ld with | Some (x,y) -> x, y | None -> assert false in let (T1 x | T2 (x,_) | T3 (x,_,_)) = t in begin match x with | B3 (e1,e2,e3) -> let p = pushBufferR e1 pr in let x' = B2 (e2,e3) in let l0 = match t with | T3 (_, d', y) -> T3 (x', d', y) | T2 (_, y) -> T2 (x', y) | T1 _ -> T1 x' in let ld = (Obj.magic pushLeft) l0 l in p, ld, mid, rd, sf | B2 (e1,e2) -> let p = pushBufferR e2 (pushBufferR e1 pr) in let l' = match t with | T3 (_, d', y) -> let l = (Obj.magic pushLeft) (T1 y) l in (Obj.magic catenate) d' l | T1 _ -> l | _ -> assert false in p, l', mid, rd, sf | _ -> assert false end | Z, Y _ | Z, U _ -> let t, r = let t, _ = popLeftNaive rd in match t with | T1 (B3 _) | T2 (B3 _, _) | T3 _ -> popLeftNaive rd | _ -> match (Obj.magic popLeft) rd with | Some (x,y) -> x, y | None -> assert false in let m1, m2 = mid in let (T1 x | T2 (x,_) | T3 (x,_,_)) = t in begin match x with | B3 (e1,e2,e3) -> let p = pushBufferR m1 pr in let mid = m2, e1 in let x' = B2 (e2,e3) in let r0 = match t with | T3 (_, d', y) -> T3 (x', d', y) | T2 (_, y) -> T2 (x', y) | T1 _ -> T1 x' in let rd = (Obj.magic pushLeft) r0 r in p, ld, mid, rd, sf | B2 (e1,e2) -> let p = pushBufferR m2 (pushBufferR m1 pr) in let mid = e1, e2 in let r' = match t with | T3 (_, d', y) -> let r = (Obj.magic pushLeft) (T1 y) r in (Obj.magic catenate) d' r | T1 _ -> r | _ -> assert false in p, Z, mid, r', sf | _ -> assert false end | _ -> assert false end | _ -> assert false and greenRight pr ld mid rd sf = match sf with | B6 (e6,e5,e4,e3,e2,e1) -> let rd = (Obj.magic pushRight) (T1 (B2 (e6,e5))) rd in pr, ld, mid, rd, B4 (e4,e3,e2,e1) | B3 _ -> begin match ld, rd with | _, Y _ | _, U _ -> let t, r = let t, _ = popRightNaive rd in match t with | T1 (B3 _) | T2 (_, B3 _) | T3 _ -> popRightNaive rd | _ -> match (Obj.magic popRight) rd with | Some (x,y) -> x, y | None -> assert false in let (T1 x | T2 (_,x) | T3 (_,_,x)) = t in begin match x with | B3 (e3,e2,e1) -> let p = pushBufferL e1 sf in let x' = B2 (e3,e2) in let r0 = match t with | T3 (y, d', _) -> T3 (y, d', x') | T2 (y, _) -> T2 (y, x') | T1 _ -> T1 x' in let rd = (Obj.magic pushRight) r0 r in pr, ld, mid, rd, p | B2 (e2,e1) -> let p = pushBufferL e2 (pushBufferL e1 sf) in let r' = match t with | T3 (y, d', _) -> let r = (Obj.magic pushRight) (T1 y) r in (Obj.magic catenate) r d' | T1 _ -> r | _ -> assert false in pr, ld, mid, r', p | _ -> assert false end | Y _, Z | U _, Z -> let t, l = let t, _ = popRightNaive ld in match t with | T1 (B3 _) | T2 (_, B3 _) | T3 _ -> popRightNaive ld | _ -> match (Obj.magic popRight) ld with | Some (x,y) -> x, y | None -> assert false in let m2, m1 = mid in let (T1 x | T2 (_,x) | T3 (_,_,x)) = t in begin match x with | B3 (e3,e2,e1) -> let p = pushBufferL m1 sf in let mid = e1, m2 in let x' = B2 (e3,e2) in let l0 = match t with | T3 (y, d', _) -> T3 (y, d', x') | T2 (y, _) -> T2 (y, x') | T1 _ -> T1 x' in let ld = (Obj.magic pushRight) l0 l in pr, ld, mid, rd, p | B2 (e2,e1) -> let p = pushBufferL m2 (pushBufferL m1 sf) in let mid = e2, e1 in let l' = match t with | T3 (y, d', _) -> let l = (Obj.magic pushRight) (T1 y) l in (Obj.magic catenate) l d' | T1 _ -> l | _ -> assert false in pr, l', mid, Z, p | _ -> assert false end | _ -> assert false end | _ -> assert false and popLeft = function | Z -> None | Y (B1 x) -> Some (x, Z) | Y b -> let x, b = popBufferL b in Some (x, Y b) | U ((B4 _ | B5 _ | B6 _ as pr), ld, mid, rd, sf) -> let x, pr = popBufferL pr in Some (x, U (pr, ld, mid, rd, sf)) | U ((B3 (x,e1,e2) as pr), Z, (m1,m2), Z, sf) -> let y = match sf with | B3 (f1,f2,f3) -> Y (B7 (e1,e2,m1,m2,f1,f2,f3)) | _ -> let f', sf = popBufferL sf in U (B3 (e1,e2,m1), Z, (m2,f'), Z, sf) in Some (x, y) | U ((B3 _ as pr), ld, mid, rd, sf) -> let pr, ld, mid, rd, sf = greenLeft pr ld mid rd sf in Some (popLeftNaive (U (pr, ld, mid, rd, sf))) | _ -> assert false and popRight = function | Z -> None | Y (B1 x) -> Some (x, Z) | Y b -> let x, b = popBufferR b in Some (x, Y b) | U (pr, ld, mid, rd, (B4 _ | B5 _ | B6 _ as sf)) -> let x, sf = popBufferR sf in Some (x, U (pr, ld, mid, rd, sf)) | U (pr, Z, (m2,m1), Z, (B3 (e2,e1,x) as sf)) -> let y = match pr with | B3 (p3,p2,p1) -> Y (B7 (p3,p2,p1,m2,m1,e2,e1)) | _ -> let f', pr = popBufferR pr in U (pr, Z, (f',m2), Z, B3 (m1,e2,e1)) in Some (x, y) | U (pr, ld, mid, rd, (B3 _ as sf)) -> let pr, ld, mid, rd, sf = greenRight pr ld mid rd sf in Some (popRightNaive (U (pr, ld, mid, rd, sf))) | _ -> assert false and catenate d1 d2 = match d1, d2 with | Z, _ -> d2 | _, Z -> d1 | Y b, _ -> begin match b with | B1 x -> pushLeft x d2 | _ -> let x, b = popBufferR b in catenate (Y b) (pushLeft x d2) end | _, Y b -> begin match b with | B1 x -> pushRight x d1 | _ -> let x, b = popBufferL b in catenate (pushRight x d1) (Y b) end | U (pr1, ld1, mid1, rd1, sf1), U (pr2, ld2, mid2, rd2, sf2) -> let y, pr2 = popBufferL pr2 and x, sf1 = popBufferR sf1 in let mid = x, y in let ld = let m1,m2 = mid1 in let pRight = Obj.magic pushRight in match sf1 with | B2 _ | B3 _ -> pRight (T3 (B2 (m1,m2), rd1, sf1)) ld1 | B4 (e1,e2,e3,e4) -> let ld'1 = pRight (T3 (B2 (m1,m2), rd1, B2 (e1,e2))) ld1 in pRight (T1 (B2 (e3,e4))) ld'1 | B5 (e1,e2,e3,e4,e5) -> let s'1 = B3 (e1,e2,e3) in let ld'1 = pRight (T3 (B2 (m1,m2), rd1, s'1)) ld1 in pRight (T1 (B2 (e4,e5))) ld'1 | _ -> assert false in let rd = let m2,m1 = mid2 in let pLeft = Obj.magic pushLeft in match pr2 with | B2 _ | B3 _ -> pLeft (T3 (pr2, ld2, B2 (m2,m1))) rd2 | B4 (e4,e3,e2,e1) -> let rd'1 = pLeft (T3 (B2 (e2,e1), ld2, B2 (m2,m1))) rd2 in pLeft (T1 (B2 (e4,e3))) rd'1 | B5 (e5,e4,e3,e2,e1) -> let s'1 = B3 (e3,e2,e1) in let rd'1 = pLeft (T3 (s'1, ld2, B2 (m2,m1))) rd2 in pLeft (T1 (B2 (e5,e4))) rd'1 | _ -> assert false in U (pr1, ld, mid, rd, sf2) let nil = Z module type Direction_T = sig val push: 'a -> 'a t -> 'a t val pop: 'a t -> ('a * 'a t) option end module A = struct let push = pushLeft let pop = popLeft end module B = struct let push = pushRight let pop = popRight end let sprintB ff = function | B1 a -> let a = ff a in Printf.sprintf "%s" a | B2 (a,b) -> let a = ff a in let b = ff b in Printf.sprintf "%s,%s" a b | B3 (a,b,c) -> let a = ff a in let b = ff b in let c = ff c in Printf.sprintf "%s,%s,%s" a b c | B4 (a,b,c,d) -> let a = ff a in let b = ff b in let c = ff c in let d = ff d in Printf.sprintf "%s,%s,%s,%s" a b c d | B5 (a,b,c,d,e) -> let a = ff a in let b = ff b in let c = ff c in let d = ff d in let e = ff e in Printf.sprintf "%s,%s,%s,%s,%s" a b c d e | B6 (a,b,c,d,e,f) -> let a = ff a in let b = ff b in let c = ff c in let d = ff d in let e = ff e in let f = ff f in Printf.sprintf "%s,%s,%s,%s,%s,%s" a b c d e f | B7 (a,b,c,d,e,f,g) -> let a = ff a in let b = ff b in let c = ff c in let d = ff d in let e = ff e in let f = ff f in let g = ff g in Printf.sprintf "%s,%s,%s,%s,%s,%s,%s" a b c d e f g | B8 (a,b,c,d,e,f,g,h) -> let a = ff a in let b = ff b in let c = ff c in let d = ff d in let e = ff e in let f = ff f in let g = ff g in let h = ff h in Printf.sprintf "%s,%s,%s,%s,%s,%s,%s,%s" a b c d e f g h let rec sprint f = function | Z -> "{}" | Y b -> Printf.sprintf "{%s}" (sprintB f b) | U (pr, ld, (m1,m2), rd, sf) -> let g = sprintT f in let pr = sprintB f pr in let ld = (Obj.magic sprint) g ld in let mid = sprintB f (B2 (m1,m2)) in let rd = (Obj.magic sprint) g rd in let sf = sprintB f sf in Printf.sprintf "{%s;%s;%s;%s;%s}" pr ld mid rd sf and sprintT f = function | T1 b -> Printf.sprintf "<%s>" (sprintB f b) | T2 (b1,b2) -> Printf.sprintf "(%s/%s)" (sprintB f b1) (sprintB f b2) | T3 (b1,q,b2) -> let b1 = sprintB f b1 in let q = (Obj.magic sprint) (fun x -> sprintT f x) q in let b2 = sprintB f b2 in Printf.sprintf "[%s|%s|%s]" b1 q b2 (*--- End of File [ cf_kotdeque.ml ] ---*) --Apple-Mail-9--911876311 Content-Transfer-Encoding: 7bit Content-Type: application/octet-stream; x-unix-mode=0644; name="cf_kotdeque.mli" Content-Disposition: attachment; filename=cf_kotdeque.mli (*---------------------------------------------------------------------------* INTERFACE cf_kotdeque.mli Copyright (c) 2003, James H. Woodyatt All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *---------------------------------------------------------------------------*) type 'a t val nil: 'a t module type Direction_T = sig val push: 'a -> 'a t -> 'a t val pop: 'a t -> ('a * 'a t) option end module A: Direction_T module B: Direction_T val catenate: 'a t -> 'a t -> 'a t val sprint: ('a -> string) -> 'a t -> string (*--- End of File [ cf_kotdeque.mli ] ---*) --Apple-Mail-9--911876311 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=US-ASCII; format=flowed > If I remember, the algorithm was described using nonuniform > data types, so that's yet another plug for supporting this more > directly in > OCaml, meaning, without having to use recursive modules, record field > polymorphism, or polymorphic records to work around the ability to > directly write such functions. The structure uses "bootstrapping" and so does my [Cf_deque] structure. I have handled bootstrapping in [Cf] with judicious use of [Obj.magic] to cast functions into the appropriate type at the recursion. Note: My [Cf] library is loaded with calls to [Obj.magic]. There are lots of places where the only way I could solve a problem was by hammering something deeply weird into a shape that would fit into the Ocaml type system. Mostly, that happens where I have an interface to C library stuff and I'm using shadow type annotations. But the bootstrapped data structures are another place. And the [Cf_gadget] module needs to do something similar (which was well known in the original Haskell code where that idea started). Every time the Ocaml team updates the compiler, I'm worried they're going to change something that invalidates my assumptions about what [Obj.magic] can and cannot do for me. I'm *not* saying they didn't warn me. I knew the job was dangerous. I'd be more exercised about the 'looping recursion' problem with List.map exploding on large data sets if I were not in the habit of using my [Cf] structures to avoid all that. -- j h woodyatt that's my village calling... no doubt, they want their idiot back. --Apple-Mail-9--911876311-- ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners