caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* my stupidity and non-tail calls
@ 1999-12-14 19:53 Norman Ramsey
  1999-12-15 10:04 ` Daniel de Rauglaudre
  1999-12-15 16:39 ` my stupidity and non-tail calls David Brown
  0 siblings, 2 replies; 6+ messages in thread
From: Norman Ramsey @ 1999-12-14 19:53 UTC (permalink / raw)
  To: caml-list

OK, I apologize for bothering everyone.  My `tail calls' are actually
sitting inside `try ... with' blocks, so I recognize that these cannot be
optimized since they are in the scope of different handlers.  I will
have to find a way to restructure my code.  A great pity, since
I like it the way it is... suggestions are welcome.

For those who wished to see the offending code, I append it, or you
can visit http://www.eecs.harvard.edu/~nr/rsync.html.


Norman


  let compress {size=size; blocks=blocks} infile =
    let blocktab = mkBlockTable blocks in
    let roll = Checksum.roll size in
    let rec compressLoop instr' b q csum infile =
      try (* first case: hit in the block table *)
        match BTab.find_all blocktab csum
        with [] -> raise Not_found
        | candidates ->
            let contents = Buffer.create size in
            let () = Queue.iter (Buffer.add_char contents) q in
            let fp = Fingerprint.string (Buffer.contents contents) in
            let (blockNum, _) = List.find (fun (_, fp') -> fp = fp') candidates in
            let instr' =
	      if Buffer.length b > 0 then STRING (Buffer.contents b) :: instr'
              else instr' in
            let instr' = BLOCK_NUMBERED blockNum :: instr' in
              ( Buffer.reset b
              ; Queue.clear q
              ; startCompressing instr' b q infile
              )
      with Not_found ->
        try
          let next = input_char infile  in
          let ()   = Queue.add next q  in
          let prev = Queue.take q  in
          let ()   = Buffer.add_char b prev  in
          let csum = roll csum prev next  in
            compressLoop instr' b q csum infile 
            (*********** not really a tail call ******)
        with End_of_file ->
          finishCompressing instr' b q
    and finishCompressing instr' b q = 
      let () = Queue.iter (Buffer.add_char b) q in
        List.rev (STRING (Buffer.contents b) :: instr')
    and startCompressing instr' b q infile =
      let rec fillAndSum csum k = 
            if k = 0 then csum
            else 
              let c = input_char infile in
                ( Queue.add c q
                ; fillAndSum (Checksum.append csum c) (k-1)
                ) in
        try 
          compressLoop instr' b q (fillAndSum (Checksum.string "") size) infile
        with End_of_file -> 
          finishCompressing instr' b q in




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

* Re: my stupidity and non-tail calls
  1999-12-14 19:53 my stupidity and non-tail calls Norman Ramsey
@ 1999-12-15 10:04 ` Daniel de Rauglaudre
  1999-12-16  7:45   ` A common use of try ... with Judicael Courant
  1999-12-15 16:39 ` my stupidity and non-tail calls David Brown
  1 sibling, 1 reply; 6+ messages in thread
From: Daniel de Rauglaudre @ 1999-12-15 10:04 UTC (permalink / raw)
  To: Norman Ramsey, caml-list

> OK, I apologize for bothering everyone.  My `tail calls' are actually
> sitting inside `try ... with' blocks, so I recognize that these cannot be
> optimized since they are in the scope of different handlers.  I will
> have to find a way to restructure my code.  A great pity, since
> I like it the way it is... suggestions are welcome.

To resolve this problem, instead of:

     try
       let x = exp in
       f x
     with Exception -> y

you can use the equivalent (but the call to "f" is tail):

      match (try Some exp with Exception -> None) with
        Some x -> f x
      | None -> y

I use that thousands of times in my code, when needed.

-- 
Daniel de RAUGLAUDRE
daniel.de_rauglaudre@inria.fr
http://cristal.inria.fr/~ddr/




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

* my stupidity and non-tail calls
  1999-12-14 19:53 my stupidity and non-tail calls Norman Ramsey
  1999-12-15 10:04 ` Daniel de Rauglaudre
@ 1999-12-15 16:39 ` David Brown
  1 sibling, 0 replies; 6+ messages in thread
From: David Brown @ 1999-12-15 16:39 UTC (permalink / raw)
  To: Norman Ramsey; +Cc: caml-list

Norman Ramsey writes:
 > OK, I apologize for bothering everyone.  My `tail calls' are actually
 > sitting inside `try ... with' blocks, so I recognize that these cannot be
 > optimized since they are in the scope of different handlers.  I will
 > have to find a way to restructure my code.  A great pity, since
 > I like it the way it is... suggestions are welcome.

I wonder if this is worthy of the FAQ.  I have done the same thing as
well.  It comes kind of as a consequence of ocaml's philosophy of
using exceptions for things such as not-found, and end of file.  Doing
this cleans up a lot of code, and a few things get a little messier.

I don't know if there is a "clean" way to do it, but what I usually do
is wrap the item around an option.

e.g.
   let maybe_input_char infile =
     try
       Some (input_char infile)
     with End_of_file ->
       None
   
   then in the code...
   
      let next' = maybe_input_char infile in
      match next' with
	Some next ->
	  let () = Queue.add next q in
	  ...
	  compressLoop instr' b q csum infile
      | None ->
	  finishCompressing instr' b q

It isn't quite as clean as the exception code, but it is
tail-recursive.

David Brown




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

* A common use of try ... with
  1999-12-15 10:04 ` Daniel de Rauglaudre
@ 1999-12-16  7:45   ` Judicael Courant
  1999-12-16 10:43     ` Daniel de Rauglaudre
  1999-12-16 11:56     ` Christophe Raffalli
  0 siblings, 2 replies; 6+ messages in thread
From: Judicael Courant @ 1999-12-16  7:45 UTC (permalink / raw)
  To: Daniel de Rauglaudre; +Cc: Norman Ramsey, caml-list


[french version below]
In his message of Wed December 15, 1999, Daniel de Rauglaudre writes: 
> To resolve this problem, instead of:
> 
>      try
>        let x = exp in
>        f x
>      with Exception -> y

I noticed that this programming pattern is quite common in Caml,
i.e. you frequently want to compute an exp that may raise an
exception, then do something with the value of this expression or
handle the exception (typically read a file, then either compute
something from what you have read or handle IO errors).

Of course, you can rewrite it as you mentionned. But should not there
be a construct to handle this kind of pattern in caml ?

This new construct (let us call it tryeval) would permit us to write
things like this :

tryeval exp
val x -> f x (* this case is evaluated only when exp evaluates to a value *)
with
(* possible exceptions *)
| Exception1 -> ...
| Exception2 y -> ...
...

which is IMHO more readable and general than the workaround you
mentionned.

In fact, this new construct could even be only a generalization of the
current try with :

try e with E1 -> e1 | ...

is indeed equivalent to

tryeval e val x -> x
with
  E1 -> e1
| ...

So that there is no need for a new keyword tryeval : one could just
reuse the existing "try" keyword, generalizing this construct.


For the moment, the easiest way for implementing this new behavior is
probably syntactic sugar : the previous example would expand to :

match (try Left exp with e -> Right e) with
| Left x -> f x
| Right Exception1 -> ...
| Right (Excepion2 y) -> ...

with definition :

type ('a,'b) sum =
| Left of 'a
| Right of 'b

Is not there anything like this in the righteous syntax yet ?

[english version above]

Dans son message du mercredi 15 décembre 1999 Daniel de Rauglaudre
écrit : 
> To resolve this problem, instead of:
> 
>      try
>        let x = exp in
>        f x
>      with Exception -> y

J'ai remarqué que ce "motif" est assez courant en Caml : il arrive
fréquemment qu'on veuille calculer une expression et gérer l'exception
levée s'il y en a une ou utiliser la valeur calculée s'il n'y a pas eu
d'exception (cas typique : lecture d'un fichier, puis calcul d'une
valeur ou gestion des erreurs d'entrées-sorties).

On peut bien sûr toujours écrire l'expression de la façon que tu
mentionnes mais ne devrait-il pas y avoir une construction primitive de
Caml pour effectuer ce genre d'opération ?

Cette nouvelle construction (qu'on peut appeler tryeval) s'utiliserait
ainsi :

tryeval exp
val x -> f x (* évalué seulement si exp ne lève pas d'exception, dans
ce cas x est la valeur de exp *)
with
(* exceptions possibles *)
| Exception1 -> ...
| Exception2 y -> ...
...

C'est à mon humble avis plus lisible et général que le truc que tu
donnes.

Cette nouvelle construction pourrait même être en fait une simple
généralisation de l'actuelle construction try ... with :

try e with E1 -> e1 | ...

serait en fait juste du sucre syntaxique pour 

tryeval e val x -> x
with
  E1 -> e1
| ...

Donc pas besoin d'un nouveau mot-clé : au lieu d'appeler cette
nouvelle construction tryeval, il suffit de l'appeler try.

Une façon simple d'implanter ce nouveau comportement serait de mettre
un tout petit peu de sucre syntaxique. L'exemple donné précédemment
donnerait :

match (try Left exp with e -> Right e) with
| Left x -> f x
| Right Exception1 -> ...
| Right (Excepion2 y) -> ...

à condition d'avoir au préalable défini :

type ('a,'b) sum =
| Left of 'a
| Right of 'b

Ca n'existe pas encore dans la syntaxe righteous ?

Judicaël.
-- 
Judicael.Courant@lri.fr, http://www.lri.fr/~jcourant/
[Computing timetable constraints..................done: 0 solution(s)]




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

* Re: A common use of try ... with
  1999-12-16  7:45   ` A common use of try ... with Judicael Courant
@ 1999-12-16 10:43     ` Daniel de Rauglaudre
  1999-12-16 11:56     ` Christophe Raffalli
  1 sibling, 0 replies; 6+ messages in thread
From: Daniel de Rauglaudre @ 1999-12-16 10:43 UTC (permalink / raw)
  To: Judicael Courant; +Cc: Norman Ramsey, caml-list

> tryeval exp
> val x -> f x (* this case is evaluated only when exp evaluates to a value *)
> with
> (* possible exceptions *)
> | Exception1 -> ...
> | Exception2 y -> ...

No problem: experiment that with Camlp4, which is exactly its application
domain and tell us your conclusions!

> tryeval exp
> val x -> f x (* évalué seulement si exp ne lève pas d'exception, dans
> ce cas x est la valeur de exp *)
> with
> (* exceptions possibles *)
> | Exception1 -> ...
> | Exception2 y -> ...

Pas de problème: expérimente ça avec Camlp4, ce qui est exactement son
domaine d'application et fais-nous part de tes conclusions!

-- 
Daniel de RAUGLAUDRE
daniel.de_rauglaudre@inria.fr
http://cristal.inria.fr/~ddr/




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

* Re: A common use of try ... with
  1999-12-16  7:45   ` A common use of try ... with Judicael Courant
  1999-12-16 10:43     ` Daniel de Rauglaudre
@ 1999-12-16 11:56     ` Christophe Raffalli
  1 sibling, 0 replies; 6+ messages in thread
From: Christophe Raffalli @ 1999-12-16 11:56 UTC (permalink / raw)
  To: Judicael Courant; +Cc: Daniel de Rauglaudre, Norman Ramsey, caml-list



On Thu, 16 Dec 1999, Judicael Courant wrote:

> tryeval exp
> val x -> f x (* this case is evaluated only when exp evaluates to a value *)
> with
> (* possible exceptions *)
> | Exception1 -> ...
> | Exception2 y -> ...
> ...
> 
> which is IMHO more readable and general than the workaround you
> mentionned.
> 

You can do this:

try raise (Val exp) with
  Val x -> f x
| Exception1 -> ...
| Exception2 y -> ...   

this is quite readable and does exactly what you want.

Is this optimized by the compiler (there is no need to construct the (Val
x) exception? If it is the case, this is a reasonable syntax. 

The main pb is that the Val exception needs to be definied for each type
as polymorphic exceptions are not allowed. 

One could make Val a reserved word (and not an exception) and propose the
following syntax:

try exp with
  Val y -> f x
| ....

The Val y case being use when no exceptions are raised.

Christophe Raffalli





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

end of thread, other threads:[~1999-12-17 21:51 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1999-12-14 19:53 my stupidity and non-tail calls Norman Ramsey
1999-12-15 10:04 ` Daniel de Rauglaudre
1999-12-16  7:45   ` A common use of try ... with Judicael Courant
1999-12-16 10:43     ` Daniel de Rauglaudre
1999-12-16 11:56     ` Christophe Raffalli
1999-12-15 16:39 ` my stupidity and non-tail calls David Brown

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