* [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
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