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 X-Original-To: caml-list@yquem.inria.fr Delivered-To: caml-list@yquem.inria.fr Received: from concorde.inria.fr (concorde.inria.fr [192.93.2.39]) by yquem.inria.fr (Postfix) with ESMTP id B263EBC0B for ; Wed, 17 Jan 2007 04:28:54 +0100 (CET) Received: from ipmail01.adl2.internode.on.net (ipmail01.adl2.internode.on.net [203.16.214.140]) by concorde.inria.fr (8.13.6/8.13.6) with ESMTP id l0H3Spm3024671 for ; Wed, 17 Jan 2007 04:28:53 +0100 Received: from ppp27-234.lns1.syd6.internode.on.net (HELO rosella) ([59.167.27.234]) by ipmail01.adl2.internode.on.net with ESMTP; 17 Jan 2007 13:58:49 +1030 X-IronPort-AV: i="4.13,198,1167571800"; d="scan'208"; a="74916435:sNHT62028897" Subject: Re: [Caml-list] Ocaml compiler features From: skaller To: Edgar Friendly Cc: caml-list@yquem.inria.fr In-Reply-To: <45AD2672.2090206@gmail.com> References: <45A87011.8080203@gmail.com> <200701141823.32855.jon@ffconsultancy.com> <20070114184148.GA26213@snarc.org> <200701142049.57959.jon@ffconsultancy.com> <20070115000544.GA28731@snarc.org> <20070115221717.GA9982@snarc.org> <1168910291.9207.62.camel@rosella.wigram> <20070116090002.GA14494@snarc.org> <1168956872.5332.28.camel@rosella.wigram> <20070116150027.GA17519@snarc.org> <1168969667.5178.18.camel@rosella.wigram> <45AD2672.2090206@gmail.com> Content-Type: text/plain Date: Wed, 17 Jan 2007 14:28:47 +1100 Message-Id: <1169004527.8941.77.camel@rosella.wigram> Mime-Version: 1.0 X-Mailer: Evolution 2.6.1 Content-Transfer-Encoding: 7bit X-Miltered: at concorde with ID 45AD97F3.000 by Joe's j-chkmail (http://j-chkmail . ensmp . fr)! X-Spam: no; 0.00; ocaml:01 compiler:01 ocaml:01 variants:01 variants:01 hashtbl:01 lexically:01 conditionals:01 nesting:01 minimise:01 lexically:01 stack:01 stack:01 edgar:98 religiously:98 On Tue, 2007-01-16 at 13:24 -0600, Edgar Friendly wrote: > skaller wrote: > > What about it? C manages to handle all that .. and has no exceptions. > > > I think the point here is that because OCaml has exceptions (that other > people use, maybe not you), it should also have finally blocks to allow > them to easily do what you do without exceptions. That is a reasonable argument to examine: but I'm not sure finally is actually all that useful. If you're thinking of: try maybe_throw X; do something finally -> cleanup done then it can be coded in Ocaml like: begin try maybe_throw X; do something; cleanup with x -> cleanup; throw x end Now you can argue the repetition of 'cleanup' is a bad, and I would agree, BUT in Ocaml this is no big deal: begin let cleanup = .. in try ... end In Java and other brain dead languages without lexical scoping you cannot do this. So Java really NEEDS finally, whereas in Ocaml the utility is less. I note C++ can use RAII: class file { FILE * f; file(string x) : f(open(x)) {} ~file() { close(f); } }; .. { file f(name); maybe throw .. } and here needs no finally, but in general in C++ you have the same problem as Java: no lexical scoping. > > But adding 'finally' may not be the best solution, > > the try/let construction looks better to me. > > > I can't find an example of this. Something about doing variable binding > as part of the try statement, and then finalizing how? I have lost the URL, sorry. But the problem is localisation: the only sane way to use exceptions is like: let result = try Some (open f) with Error -> None in match result with None -> ... Some .... If you try to do it like: try before open f middle close f after with _ -> close f then you have all sorts of problems if a different exception is thrown in the before or after parts. Using type information to distinguish the kind of exception is clearly a hack: what if you're opening two files? So roughly, this structure is not amenable to composition. Adding 'finally' doesn't solve that problem, and that problem is far worse than the lack of finally. [Sorry this is a really bad explanation ..] > > But typically I do that kind of > > thing the C way, which is generally the Ocaml way too: > > you propagate return codes from functions using variants > > up call chains. > > > The OCaml standard library seems to be programmed using exceptions over > variants. I'll have to say I don't like or use exceptions much, but I'm > forced to by the standard libraries. Yes, but you can do what many people do: map the exceptions into variants like: let hfind x y = try Some(Hashtbl.find x y) with Not_found -> None religiously, eliminating the exceptions. And you can convert to exceptions from variants like: let efind x y = match hfind x y with | Some r -> r | None -> raise Not_found So really the library gives one of two cases, and allows you to code the other easily. You aren't forced to use "long throw" handlers, i.e. handlers which are a long way from the point of the throwing code lexically. The wrapper above eliminates any need for a finally clause since ordinary conditionals and matches can be used instead -- in the case you're converting to variants. It still leaves open the case of "long throw" exceptions where you want to use exceptions: that may well benefit from a finally clause but you see now the number of use cases is reduced because the wrapper technique will be appropriate in some cases. IN FACT the problem is not entirely an issue with exceptions! Examining the 'exception free' case using fopen below we have the SAME problem: let fopen f lock = try Some (open f) with Error -> None in let lck = mutex () in lock mutex; let f = fopen name lck in match f with | Some file -> ... close, release lck | None -> release lck WOOPS! Again we had to repeat the cleanup code. Releasing the mutex AFTER the whole code works, but if we want separate control flows for the two cases we have to test the variant again: begin match f with .. end; release lck; match f with .. (* fork again *) So actually, you could use a 'finally' here too: the problem doesn't appear to be special to exception handling, but rather a case where you have a control fork and want some code done in the middle of both branches of the fork. Roughly .. block structure (nesting) can't handle interleaved execution very cleanly: you can minimise the impact with lexically scoped subroutine call, but you still need to call it twice. Whereas with 'finally' clause you don't, the problem is that you're restricted to the balanced (block) structure so the code location of the 'finally' clause is right for both branches. In complex cases .. the finally clause isn't useful because it imposes too much nested structure that turns out to be wrong anyhow: you have a state machine which just can't be represented with a pushdown stack, not even one that supports a second control path via stack unwinding. Please note again I'm not arguing against adding finally (though I might do so!) I'm arguing the need for it isn't as common as you might think in Ocaml, and part of the need is only a symptom of a wider problem. Some other languages provide continuations instead of exceptions, and it is much easier to manage control flow in such a language, since in effect 'goto' targets are first class (can be stored in variables). I guess it is also easier to *lose* control too :) -- John Skaller Felix, successor to C++: http://felix.sf.net