caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* Stricter version of #use ?
@ 2009-03-24 18:00 Harrison, John R
  2009-03-24 22:25 ` Zheng Li
  0 siblings, 1 reply; 4+ messages in thread
From: Harrison, John R @ 2009-03-24 18:00 UTC (permalink / raw)
  To: OCaml

I'd like a variant of the #use directive for reading in an OCaml
source file, but with the property that it halts immediately on the
first error anywhere in a nesting of #use'd files. For example,
suppose you have a file "root.ml" containing

  let x = 1;;

  #use "branch.ml";;

  let y = 2;;

and a file "branch.ml" containing

  let u = 3;;

  let v = failwith "X";;

  let w = 4;;

Then I want the following to stop immediately on the failure inside
"branch.ml" and hence not evaluate the "y = 2" line in "root.ml" as it
currently does:

          Objective Caml version 3.10.0

  # #use "root.ml";;
  val x : int = 1
  val u : int = 3
  Exception: Failure "X".
  val y : int = 2

On the other hand, I'd want it to keep the results of the x = 1 and
u = 3 lines, not roll back everything. Is this easy to accomplish?
(By the way, I'm ultimately interested in using this together with
camlp5, and possibly with the new camlp4, if that makes a difference.)

John.


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

* Re: Stricter version of #use ?
  2009-03-24 18:00 Stricter version of #use ? Harrison, John R
@ 2009-03-24 22:25 ` Zheng Li
  2009-03-25  0:14   ` Harrison, John R
  0 siblings, 1 reply; 4+ messages in thread
From: Zheng Li @ 2009-03-24 22:25 UTC (permalink / raw)
  To: Harrison, John R; +Cc: OCaml

Hi,

On 3/24/2009 7:00 PM, Harrison, John R wrote:
> Then I want the following to stop immediately on the failure inside
> "branch.ml" and hence not evaluate the "y = 2" line in "root.ml" as it
> currently does:
>
>            Objective Caml version 3.10.0
>
>    # #use "root.ml";;
>    val x : int = 1
>    val u : int = 3
>    Exception: Failure "X"
>    val y : int = 2

I agree this doesn't look like an intuitive semantics.

> On the other hand, I'd want it to keep the results of the x = 1 and
> u = 3 lines, not roll back everything. Is this easy to accomplish?

I just want to say rolling back everything is another 
consistent/intuitive semantics.

> (By the way, I'm ultimately interested in using this together with
> camlp5, and possibly with the new camlp4, if that makes a difference.)

You may try the following code snippet. It's not a total solution but an 
ad-hoc workaround. It only deals with recursive "#use" like in your 
example (e.g., "#load" operation inside a "#use" script will still 
behave the same as before), though it's not difficult to adapt the other 
primitives similarly ("#load" is probably the only one wanted, let me 
know if you need any help on that).

The only advantage of this method is that you don't have to modify the 
compiler code. You can just copy&paste the code into your toplevel 
sessions and go, or add it into your .ocamlinit file if you need it all 
the time.

----
Hashtbl.replace Toploop.directive_table "use"
   (Toploop.Directive_string begin
      let is_top = ref true in
      fun name ->
        let top_env = !is_top in
        is_top := false;
        let succ = Toploop.use_file Format.std_formatter name in
        is_top := top_env;
        if not !is_top && not succ then raise Exit
    end)
----

HTH.

--
Zheng


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

* RE: Stricter version of #use ?
  2009-03-24 22:25 ` Zheng Li
@ 2009-03-25  0:14   ` Harrison, John R
  2009-03-25  8:32     ` Zheng Li
  0 siblings, 1 reply; 4+ messages in thread
From: Harrison, John R @ 2009-03-25  0:14 UTC (permalink / raw)
  To: Zheng Li; +Cc: OCaml

Hi Zheng,

| You may try the following code snippet. It's not a total solution
| but an ad-hoc workaround. It only deals with recursive "#use" like
| in your example (e.g., "#load" operation inside a "#use" script
| will still behave the same as before), though it's not difficult
| to adapt the other primitives similarly ("#load" is probably the
| only one wanted, let me know if you need any help on that).

Thanks, that's a clever solution! Just one more question, though. I'd
like to exploit this alternative "use" function in a slightly more
programmatic way so I also want it as an OCaml function. (Then I can
search non-standard paths, do basic conditional loading etc.) So I
adapted your solution slightly and put the following in my .ocamlinit
file to give myself a function "use_file" that (so I thought) would
correspond to the #use directive:

  let use_file =
    let is_top = ref true in
    fun name ->
      let top_env = !is_top in
      is_top := false;
      let succ = Toploop.use_file Format.std_formatter name in
      is_top := top_env;
      if not !is_top && not succ then
        (Format.print_string("Error in included file "^name);
         Format.print_newline();
         raise Exit)
      else ();;
  
  Hashtbl.replace Toploop.directive_table "use"
    (Toploop.Directive_string use_file);;
  
So with my "root.ml" and "branch.ml" files from the first message, it
all works exactly as I wanted, a nice early failure with a clear
"traceback":

  # #use "root.ml";;
  val x : int = 1
  val u : int = 3
  Exception: Failure "X".
  Error in included file branch.ml
  # x;;
  - : int = 1
  # u;;
  - : int = 3

Yet if I replace the line

  #use "branch.ml";;

in "root.ml" with what I supposed would be equivalent:

  use_file "branch.ml";;

then the exception propagates out and I get the "rollback" that
I wanted to avoid:

  # #use "root.ml";;
  val x : int = 1
  val u : int = 3
  Exception: Failure "X".
  Error in included file branch.ml
  Exception: Pervasives.Exit.
  # x;;
  - : int = 1
  # u;;
  Unbound value u

Any idea why that should be?

John.


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

* Re: Stricter version of #use ?
  2009-03-25  0:14   ` Harrison, John R
@ 2009-03-25  8:32     ` Zheng Li
  0 siblings, 0 replies; 4+ messages in thread
From: Zheng Li @ 2009-03-25  8:32 UTC (permalink / raw)
  To: Harrison, John R; +Cc: OCaml

Hi John,

On 3/25/2009 1:14 AM, Harrison, John R wrote:
> So with my "root.ml" and "branch.ml" files from the first message, it
> all works exactly as I wanted, a nice early failure with a clear
> "traceback":
>
>    # #use "root.ml";;
>    val x : int = 1
>    val u : int = 3
>    Exception: Failure "X".
>    Error in included file branch.ml
>    # x;;
>    - : int = 1
>    # u;;
>    - : int = 3
>
> Yet if I replace the line
>
>    #use "branch.ml";;
>
> in "root.ml" with what I supposed would be equivalent:
>
>    use_file "branch.ml";;
>
> then the exception propagates out and I get the "rollback" that
> I wanted to avoid:
>
>    # #use "root.ml";;
>    val x : int = 1
>    val u : int = 3
>    Exception: Failure "X".
>    Error in included file branch.ml
>    Exception: Pervasives.Exit.
>    # x;;
>    - : int = 1
>    # u;;
>    Unbound value u
>
> Any idea why that should be?

The execution difference between a toplevel phrase like "use_file xxx" 
and a toplevel directive like "#use xxx" is subtle. The current toplevel 
implementation always execute a toplevel phrase in a protected 
environment, i.e. the execution effect of a single toplevel phrase is 
always all-or-nothing. And this is, AFAIK, hard-wired in the code and I 
don't know how to tweak it without modifying the toplevel source. On the 
other hand, the toplevel won't protect the execution of a directive, 
rather it relies on the directives to protect themselves --- that's why 
we can still do some little trick without modifying the toplevel source. 
So in the end, you can't partly keep the execution effect of a toplevel 
phrase like "use_file xxx".

Moreover, I can't see how we can avoid this (if we intend to use 
"use_file" as a function rather than a directive), no matter how we 
define "use_file". If the "use_file" fails upon some inside errors, the 
whole effect of its execution will be aborted due to the protected 
environment; if we catch the errors ourselves, i.e. the whole execution 
still returns successfully despite the inside error, then the rest of 
the code (below "use_file xxx") will continue to run.

The only solution (without modifying toplevel) is to define "use_file" 
as a function always succeeds and returns a flag telling whether the 
execution is all successful. Then it will be the responsibility of 
programmers to decide to abort or continue or reset. Something like:

val use_file: string -> (unit -> unit) option

it returns either None on full success or Some reset to let the 
programmers choose whether to abort (raise Exit) or (reset ()).

Regards
--
Zheng



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

end of thread, other threads:[~2009-03-25  8:31 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-03-24 18:00 Stricter version of #use ? Harrison, John R
2009-03-24 22:25 ` Zheng Li
2009-03-25  0:14   ` Harrison, John R
2009-03-25  8:32     ` Zheng Li

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