caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] beginner question about camlp4
@ 2002-01-23  2:45 Doug Bagley
  2002-01-23 10:15 ` Daniel de Rauglaudre
  2002-01-23 19:43 ` Charles Martin
  0 siblings, 2 replies; 5+ messages in thread
From: Doug Bagley @ 2002-01-23  2:45 UTC (permalink / raw)
  To: Caml List

Greetings,

I'm just learning about camlp4, and I was wondering if anyone would
care to comment on how I'm starting to go about it. If neither the
subject of camlp4, nor reading the random wanderings of newbies is of
interest, then please hit "n" now :)

First, I chose a simple problem: extending the exception syntax with
an "always" clause, to allow easy cleanup. My motivation came from
haing previously written a few I/O routines that look sort of like
this:

  let chan = open_something () in
  try
    process chan;
    close chan
  with
    Ouch -> close chan; maybe_do_something
  | e    -> close chan; raise e

(BTW, this finalization idiom is also mentioned in the caml docs). 
It works, but hey, it looks like we repeat ourselves calling "close
chan"! So, I thought maybe I could use camlp4 to write the code like
this instead:

  let chan = open_something () in
  try
    process chan;
  always
    close chan
  with
    Ouch -> maybe_do_something
  | e    -> raise e

Which I think looks a little cleaner. (Or, anyway provides a good
exercise for learning camlp4 :)

In my first attempt I decided to use an inner try block to catch all
exceptions. In its expanded form, it specifies the cleanup expression
(see $a$ below) in only 2 places, like so:

  EXTEND
    expr: LEVEL "expr1"
    [[ "try"; e = expr; "always"; a = expr; 
       "with"; OPT "|"; l = LIST1 
       [[ x1 = patt; w = OPT [ "when"; e = expr -> e ]; "->";
          x2 = expr -> (x1, w, x2 )
       ]] SEP "|" -> 
       <:expr<
          try do {
            try do {$e$; $a$} with ex -> do {$a$; raise ex}
          } with [$list:l$] >>
    ]];
  END;;

(The biggest reason I chose this implementation first was that it
wasn't immediately apparent to me how to manipulate $list:l$ to
produce code like what I originally started with). 

In this case, the produced code will look something like this:

  let chan = open_in "file" in
  try
    try
      begin process chan end;
      close chan
    with
      ex -> close chan; raise ex
  with
    Ouch -> maybe_do_something
  | e -> raise e;;
  
After learning a little more, I found out it wasn't so hard to get
what I'd originally planned, like this:

  EXTEND
    expr: LEVEL "expr1"
    [[ "try"; e = expr; "always"; a = expr; 
       "with"; OPT "|"; l = LIST1 
       [[ x1 = patt; w = OPT [ "when"; e = expr -> e ]; "->"; 
  	x2 = expr -> (x1, w, x2)
       ]] SEP "|" ->
       let l = List.map (fun (g,h,i) -> (g,h,<:expr< do{$a$;$i$} >>)) l in
         <:expr<  try do { $e$; $a$ } with [$list:l$] >>
    ]];
  END;;
  
Which produces code that looks something like this:
  
  let chan = open_in "file" in
  try
    begin process chan end;
    close chan
  with
    Ouch -> close chan; maybe_do_something
  | e -> close chan; raise e;;

Which looks good to me.


So my questions to the gurus are:
- Is one solution particularly better than the other?  (Do I lose or
  gain anything by using the inner try block?)
- Have I written any bad camlp4 here?  Please let me know if I've used
  anything in an incorrect manner or am guilty of bad style.
- Is this a worthwhile use of camlp4?  


P.S. Incidentally, before I tried camlp4, I had come up with a HOF to
encapsulate cleanup for exceptions, like this:

let safely setup cleanup subject f =
  let x = setup subject in
  try f x; cleanup x with e -> cleanup x; raise e

Which threads a value x through the setup/processing/cleanup functions,
and works fine for processing files, for instance, where x is the opened
channel, for example:

  safely open_in close_in "somefile" process_channel;

But in complicated situations, using this function becomes a little
hard to follow, like if I'm nesting a few file opens.

I happened to notice that FORT also uses pretty much the same HOF to
do cleanups too, so maybe I'm not the only one who thinks simplifying
finalization is a nice thing?

cheers,
doug
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* Re: [Caml-list] beginner question about camlp4
  2002-01-23  2:45 [Caml-list] beginner question about camlp4 Doug Bagley
@ 2002-01-23 10:15 ` Daniel de Rauglaudre
  2002-01-23 19:43 ` Charles Martin
  1 sibling, 0 replies; 5+ messages in thread
From: Daniel de Rauglaudre @ 2002-01-23 10:15 UTC (permalink / raw)
  To: Caml List

Hi,

On Tue, Jan 22, 2002 at 08:45:10PM -0600, Doug Bagley wrote:

>   let chan = open_in "file" in
>   try
>     begin process chan end;
>     close chan
>   with
>     Ouch -> close chan; maybe_do_something
>   | e -> close chan; raise e;;

No problem about your Camlp4 code, and it is indeed a possible usage
of Camlp4. But I would have written a different construction, because
I consider than your first "close chan" should be outside the try and
I would like to be able to return something (get the result of your
"process chan").

My construction would be something like this:
     some_expression always some_cleanup

which would be converted into:
     let x = try some_expression with e -> some_cleanup; raise e in
     some_cleanup;
     x

Well, but perhaps an infix construction is not very readable here.

> let safely setup cleanup subject f =
>   let x = setup subject in
>   try f x; cleanup x with e -> cleanup x; raise e

For the same reason, I would prefer:

 let safely setup cleanup subject f =
   let x = setup subject in
   let r = try f x with e -> cleanup x; raise e in
   cleanup x;
   r

This way, "safely" could return the value of "f x" when it is ok.

Now, Camlp4 or function "safely", it is a matter of personal opinion.

-- 
Daniel de RAUGLAUDRE
daniel.de_rauglaudre@inria.fr
http://cristal.inria.fr/~ddr/
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* Re: [Caml-list] beginner question about camlp4
  2002-01-23  2:45 [Caml-list] beginner question about camlp4 Doug Bagley
  2002-01-23 10:15 ` Daniel de Rauglaudre
@ 2002-01-23 19:43 ` Charles Martin
  2002-01-23 20:00   ` Doug Bagley
  1 sibling, 1 reply; 5+ messages in thread
From: Charles Martin @ 2002-01-23 19:43 UTC (permalink / raw)
  To: Doug Bagley, Caml List


>P.S. Incidentally, before I tried camlp4, I had come up with a HOF to
>encapsulate cleanup for exceptions, like this:
>
>let safely setup cleanup subject f =
>  let x = setup subject in
>  try f x; cleanup x with e -> cleanup x; raise e
>
>But in complicated situations, using this function becomes a little
>hard to follow, like if I'm nesting a few file opens.

I think it would be easier if you broke your HOFs down slightly differently.  I use the following "base" HOF:

let protect f always =
  try let r = f () in always (); r
  with e -> always (); raise e

For file handling, I then use the following:

let with_input_file fname f =
  let chan = open_in fname in
  protect (f chan) (fun () -> close_in chan)

With a similar "with_output_file".  Typical code then looks like this:

let foo infile outfile =
  with_input_file infile
    (fun inchan -> with_output_file outfile
      (fun outchan -> [...] ))

Charles


_________________________________________________________
Do You Yahoo!?
Get your free @yahoo.com address at http://mail.yahoo.com

-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* Re: [Caml-list] beginner question about camlp4
  2002-01-23 19:43 ` Charles Martin
@ 2002-01-23 20:00   ` Doug Bagley
  2002-01-23 21:48     ` Charles Martin
  0 siblings, 1 reply; 5+ messages in thread
From: Doug Bagley @ 2002-01-23 20:00 UTC (permalink / raw)
  To: Charles Martin; +Cc: Caml List

Charles Martin wrote:
> let protect f always =
>   try let r = f () in always (); r
>   with e -> always (); raise e
>
> For file handling, I then use the following:
> 
> let with_input_file fname f =
>   let chan = open_in fname in
>   protect (f chan) (fun () -> close_in chan)

Are you sure that's right?  I think (f chan) executes before the call
to protect, and you want protect to execute f:

let with_input_file fname f =
  let chan = open_in fname in
  protect (fun () -> f chan)
     (fun () -> prerr_endline ("closing " ^ fname); close_in chan)

???

I have also developed similar "with_input_from" kind of functions, but
I use Unix.dup/dup2 to switch out the filedescriptors so the functions
that run under them can just read/write from stdin/stdout, instead of
needing a specific channel.  Seems like a common idiom, is it from
lisp?  I forget.  You're welcome to look at my versions here:

 http://www.bagley.org/~doug/ocaml/io_lib/

Of course, maybe I'm the one who is not doing it right.

cheers,
doug
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* Re: [Caml-list] beginner question about camlp4
  2002-01-23 20:00   ` Doug Bagley
@ 2002-01-23 21:48     ` Charles Martin
  0 siblings, 0 replies; 5+ messages in thread
From: Charles Martin @ 2002-01-23 21:48 UTC (permalink / raw)
  To: Doug Bagley; +Cc: Caml List


>Are you sure that's right?  I think (f chan) executes before the call
>to protect, and you want protect to execute f:

That's my punishment for not cutting and pasting actual code :)

let with_input_file fname f =
  let chan = open_in fname in
  protect (fun () -> f chan) (fun () -> close_in chan)


_________________________________________________________
Do You Yahoo!?
Get your free @yahoo.com address at http://mail.yahoo.com

-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

end of thread, other threads:[~2002-01-24 15:52 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-01-23  2:45 [Caml-list] beginner question about camlp4 Doug Bagley
2002-01-23 10:15 ` Daniel de Rauglaudre
2002-01-23 19:43 ` Charles Martin
2002-01-23 20:00   ` Doug Bagley
2002-01-23 21:48     ` Charles Martin

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