caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* RE: [Caml-list] Checking for eof
@ 2004-12-26 15:25 Don Syme
  2004-12-27  0:14 ` skaller
  0 siblings, 1 reply; 5+ messages in thread
From: Don Syme @ 2004-12-26 15:25 UTC (permalink / raw)
  To: Nicolas George, Caml mailing list; +Cc: Andrew Kennedy, Nick Benton


Nick Benton and Andrew Kennedy have addressed this rather under-appreciated deficiency in ML exceptions in their paper "Exceptional Syntax"

http://research.microsoft.com/~akenn/sml/ExceptionalSyntax.pdf

The topic has also been raised here before http://caml.inria.fr/archives/200407/msg00028.html 

I think this is so sensible that it should be adopted in all variants of ML. 

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

Only the bindings are covered by the "try".  e.g.

let readfile chan =
  let rec loop rlst =
    let try line = input_line chan
        in loop (line :: rlst)
        with End_of_file -> List.rev rlst
  in
    loop []

(note: reduces 17 lines to 7)

Another possibility might be 

  "let try <bindings> 
   with <matching>
   in <expr> "

or indeed you could support both of the above, leaving it up to the programmer to choose where to place the ever-awkward handling code. Unfortunately the syntax 

  "try let <bindings> 
   in <expr> 
   with <matching>"

is too ambiguous when iterated "let ... in" bindings are used.

Don


-----Original Message-----
From: caml-list-admin@yquem.inria.fr [mailto:caml-list-admin@yquem.inria.fr] On Behalf Of Nicolas George
Sent: 26 December 2004 14:09
To: Caml mailing list
Subject: Re: [Caml-list] Checking for eof

Le sextidi 6 nivôse, an CCXIII, briand@aracnet.com a écrit :
>       try
>         (input_line chan), false
>       with
>         | End_of_file -> "", true

I would have written that

	try
	  Some (input_line chan)
	with
	  | End_of_file -> None

but the idea is the same. I find it is an irritating limitation of OCaml
syntax to have to pack and then unpack all local values in order to uncatch
exceptions. Something like

	try
	  let line = input_line chan in
	  untry
          loop (line :: rlst)
	with
	  | End_of_file -> List.rev rlst

This syntax is somewhat awkward: untry is neither a third member of the
try...with structure, because it must be inside the flow of let...in
declaration, nor a stand-alone statement, because it must not be allowed
anywhere outside try...with.

On the contrary, as far as I can see, the semantics is quite simple.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* RE: [Caml-list] Checking for eof
  2004-12-26 15:25 [Caml-list] Checking for eof Don Syme
@ 2004-12-27  0:14 ` skaller
  0 siblings, 0 replies; 5+ messages in thread
From: skaller @ 2004-12-27  0:14 UTC (permalink / raw)
  To: caml-list

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




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Caml-list] Checking for eof
  2004-12-26 13:09   ` Nicolas George
@ 2004-12-27 20:23     ` Martin Jambon
  0 siblings, 0 replies; 5+ messages in thread
From: Martin Jambon @ 2004-12-27 20:23 UTC (permalink / raw)
  To: Nicolas George; +Cc: Caml mailing list, Don Syme

On Sun, 26 Dec 2004, Nicolas George wrote:

> Le sextidi 6 nivôse, an CCXIII, briand@aracnet.com a écrit :
> >       try
> >         (input_line chan), false
> >       with
> >         | End_of_file -> "", true
>
> I would have written that
>
> 	try
> 	  Some (input_line chan)
> 	with
> 	  | End_of_file -> None
>
> but the idea is the same. I find it is an irritating limitation of OCaml
> syntax to have to pack and then unpack all local values in order to uncatch
> exceptions. Something like
>
> 	try
> 	  let line = input_line chan in
> 	  untry
>           loop (line :: rlst)
> 	with
> 	  | End_of_file -> List.rev rlst
>
> This syntax is somewhat awkward: untry is neither a third member of the
> try...with structure, because it must be inside the flow of let...in
> declaration, nor a stand-alone statement, because it must not be allowed
> anywhere outside try...with.
>
> On the contrary, as far as I can see, the semantics is quite simple.

Instead of this (try too large):

let readfile chan =
  let rec loop rlst =
    try
      let line = input_line chan in
      loop (line :: rlst)
    with
	End_of_file -> List.rev rlst in
  loop []

We can write that:

let readfile chan =
  let rec loop rlst =
    (try
       let line = input_line chan in
       fun () -> loop (line :: rlst)
     with
	 End_of_file -> fun () -> List.rev rlst) () in
  loop []


And it seems to be handled efficiently by the compiler (confirmation?).
(and for the desperate, it is not difficult to write a Camlp4 syntax
extension which does this :-)


Martin

--
Martin Jambon, PhD
Researcher in Structural Bioinformatics since the 20th Century
The Burnham Institute http://www.burnham.org
San Diego, California





^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Caml-list] Checking for eof
  2004-12-26  1:58 ` [Caml-list] " briand
@ 2004-12-26 13:09   ` Nicolas George
  2004-12-27 20:23     ` Martin Jambon
  0 siblings, 1 reply; 5+ messages in thread
From: Nicolas George @ 2004-12-26 13:09 UTC (permalink / raw)
  To: Caml mailing list

[-- Attachment #1: Type: text/plain, Size: 886 bytes --]

Le sextidi 6 nivôse, an CCXIII, briand@aracnet.com a écrit :
>       try
>         (input_line chan), false
>       with
>         | End_of_file -> "", true

I would have written that

	try
	  Some (input_line chan)
	with
	  | End_of_file -> None

but the idea is the same. I find it is an irritating limitation of OCaml
syntax to have to pack and then unpack all local values in order to uncatch
exceptions. Something like

	try
	  let line = input_line chan in
	  untry
          loop (line :: rlst)
	with
	  | End_of_file -> List.rev rlst

This syntax is somewhat awkward: untry is neither a third member of the
try...with structure, because it must be inside the flow of let...in
declaration, nor a stand-alone statement, because it must not be allowed
anywhere outside try...with.

On the contrary, as far as I can see, the semantics is quite simple.

[-- Attachment #2: Type: application/pgp-signature, Size: 185 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [Caml-list] Checking for eof
  2004-12-26  1:12 romildo
@ 2004-12-26  1:58 ` briand
  2004-12-26 13:09   ` Nicolas George
  0 siblings, 1 reply; 5+ messages in thread
From: briand @ 2004-12-26  1:58 UTC (permalink / raw)
  To: romildo; +Cc: caml-list


Youa are actually looking for an eof exception.

Here is a code segment which demonstrates this nicely and won't blow
up the stack.

let readfile chan =
  let rec loop rlst =
    let line, eof = 
      try
        (input_line chan), false
      with
        | End_of_file -> "", true
    in
      if not eof then
	(
          loop (line :: rlst);
	)
      else
        List.rev rlst
  in
    loop []
;;

Brian


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2004-12-27 20:23 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-12-26 15:25 [Caml-list] Checking for eof Don Syme
2004-12-27  0:14 ` skaller
  -- strict thread matches above, loose matches on Subject: below --
2004-12-26  1:12 romildo
2004-12-26  1:58 ` [Caml-list] " briand
2004-12-26 13:09   ` Nicolas George
2004-12-27 20:23     ` Martin Jambon

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