Version française
Home     About     Download     Resources     Contact us    
Browse thread
RE: [Caml-list] Checking for eof
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: skaller <skaller@u...>
Subject: RE: [Caml-list] Checking for eof
On Mon, 2004-12-27 at 02:25, Don Syme wrote:

> An OCaml or F# version of their construct might be 
>   "let try <bindings> 
>    in <expr> 
>    with <matching>"

> Another possibility might be 
> 
>   "let try <bindings> 
>    with <matching>
>    in <expr> "
> 

let readfile chan = 
  let x = ref [] in 
  try while true do x:= input_line chan :: !x done
  with End_of_file -> List.rev !x

is only 4 lines :)

However, this is a bad use of exceptions, IMHO,
since End_of_file isn't an error, and when thrown
should never propagate far. With an option type instead:

let readfile chan =
  let rec loop rlst = 
    match input_line chan with
    | None -> List.rev rlst
    | Some x -> loop (x::rlst)
  in loop []

is 6 lines and clearly tailrec.

---------------------

Here is what Felix does:

proc f() {
  g handler;

  noreturn proc handler (x:int) { 
    print "Error "; print x; endl;
    goto endoff;
  }

endoff:>
}

Here, the handler is *passed* to the routine g,
which calls it in event of an exceptional situation,
the handler code then 'throws' by doing a non-local goto.
(This unwinds the stack).

This technique has a couple of advantages,
the most important one being that it is *impossible*
to fail to catch an exception. If code can 'throw'
an exception, the type system ensures you pass it
a handler.

There is also no problem with creating 'exception types',
handlers are just ordinary procedures.

Finally an actual 'pragmatic' test on Alioth Shoothout
had the Felix version of the test banned because it
totally wiped out all other solutions on performance.
I don't mean it was a bit faster -- it was orders
of magnitude faster.

The reason is the test was too simple, and the optimiser
managed to reduce the whole test to a flat loop,
the goto became local (local gotos in Felix become gotos in C).

Optimisation of dynamic exceptions is very hard.
This is because the raise and catch points are entirely
decoupled statically: the coupling is entirely dynamic
based on RTTI (even in Ocaml).

The Felix technique on the other hand combines
static scoping with dynamic argument passing
in the *usual* way.

It is much easier to reason about, provided
the goto target is sensibly placed.

It remains to encapsulate this technique in a nicer
syntax than 'goto' for example something like:


proc f() {
  exception handler (x:int) { 
    print "Error "; print x; endl;
  }

  g handler;
}

Note the handler of course doesn't have to
be passed in the case it would be in scope anyhow,
in which case the same syntax can be used to
obtain fully static exception handling.

[Felix doesn't have a way to put 'noreturn' in the typing,
which would be needed to be sure when you 'throw' an
exception it doesn't return]

In particular, the 'analogue' of dynamic exception
handling is obtained by storing the handler in a
global variable, which means anyone can jump to it.
This is the same as having an exception represented
by a globally agreed on 'exception type'.

What happens if the handler stack frame is already
popped? Well, in Felix the handler runs fine,
and the goto works (the stack frames are all heap
allocated and garbage collected) -- however the
falling off the end of the enclosing dead procedure
terminates the program (because the return address
is reset to NULL on returning, so the GC can
collect the caller)

If this sounds bad -- this is exactly what an 
uncaught exception does.

And now you can see, using the Felix technique
as a model, some indication why dynamic EH is so bad. 
It is equivalent to using a global variable for
something that *should* be local.

The difference is that 'variable' in Felix can
be localised as required, and the localisation
is then supported by the type system.

This technique can of course by used in Ocaml.
Although there is no 'goto' you can emulate it
with exceptions (LOL)

exception goto;
let f () =
  let handler i = 
    print_endline ("Error " ^ string_of_int i);
    raise goto;
  in
  try g handler
  with goto -> ()

It may be possible to leverage local modules to
provide some way to localise the exception
and guarrantee statically it can't escape
(possibly allowing Ocaml to optimise..?)


-- 
John Skaller, mailto:skaller@users.sf.net
voice: 061-2-9660-0850, 
snail: PO BOX 401 Glebe NSW 2037 Australia
Checkout the Felix programming language http://felix.sf.net