I want to do something like the following but in ocaml,
I someone can give an example code?
I want to do something like the following but in ocaml,
I someone can give an example code?
Iām not sure about how you define nice, but this is how Iād do it:
(* break *)
exception Break
let () =
try
for i = 0 to 9 do
if i = 4 then
raise Break
else
Printf.printf "%d\n%!" i
done
with
| Break -> ()
(* continue *)
exception Continue
let () =
for i = 0 to 9 do
try
if i == 4 then
raise Continue;
Printf.printf "%d\n%!" i
with
| Continue -> ()
done
Exit
is an exception in stdlib defined exactly for this use case. Itās not raised by any library function so you can be sure youāre the one who caused it.
I am not sure from your recent questions whether it is ocamlās facilities for dealing with imperative programming and mutable state which are perplexing you, or whether rather it is functional programming which is perplexing you.
If the former, then the examples you have been given are fine, although many might defer before calling it āniceā.
If the latter, the following is the more functional way to implement a break or continue equivalent in ocaml to the example C code to which you have referred, using iterative recursion. For your ābreakā example in C:
let () =
let rec loop i =
if i <> 4 then begin
Printf.printf "%d\n" i ;
loop (i + 1) end in
loop 0
For the ācontinueā example in C:
let () =
let rec loop i =
if i = 4 then loop (i + 1)
else if i < 10 then begin
Printf.printf "%d\n" i ;
loop (i + 1) end in
loop 0
Is there anything here which would benefit from further explanation?
Edit For the ācontinueā example this would be more efficient although less faithful to the layout of the C code:
let () =
let rec loop i =
if i < 10 then begin
if i <> 4 then Printf.printf "%d\n" i ;
loop (i + 1) end in
loop 0
@cvine Why is your code in the edit more efficient ? Although the tests ā<10ā and ā=4ā are done in different orders, it looks like they are done as often in both examples.
Yes, I misspoke. āMore obviousā perhaps, in having a single point of recursion, rather than āmore efficientā (save that it does omit the test against 4 when the count is 10).
In this sort of situation, raise
ought to be replaced by raise_notrace
IMO. The distinction is that raise_notrace
just affects control flow but does not involve the backtrace mechanism used to report unhandled exceptions. Unless the backtrace mechanism is disabled, raise_notrace
is more efficient. It also does not clobber the existing state of the backtrace, and so (functions that call) raise_notrace
can be used in exception handlers.
Also, while raise
and raise_notrace
are fast relative to the analogues in many other languages, pushing a trap handler (which has to be done every time a try
expression is evaluated) is not entirely free. So do not be surprised if the let rec
versions are slightly faster. On the other hand, returning early using an exception really shines when it jumps over the return from many pending non-tail function calls.
Can i consider āraiseā as a āgotoā where the ācontextā is dropped.
Iām curious, would using local exceptions here be any more efficient?
let f () =
let exception E of int in
try for i = 0 to 10 do
if Int.(i = 5) then raise E i
done;
0
with E i -> i
Whenever I have exceptions required for these kinds of early exits, I always use local exceptions, because I assume that it would provide additional information to the compiler - guaranteeing that the exceptions must have been generated from this particular invokation of the function.
I understand that vanilla OCaml doesnāt really do much optimisations, but would something like Flambda be able to make use of this?
Currently, no. In fact, local exceptions are only different from non-local exceptions during the scope resolution phase. Itās not an error to have a local exception escape its scope (either by raising it or returning is as a regular value, eventually embedded in a bigger value), and it is even possible to do a bit of introspection on out-of-scope exceptions to recover the constructor name for instance.
We do have some optimisations in flambda 2 when we can match a raise point with its handler statically, but itās not specific to local exceptions. In your example we would be able to remove completely the exception mechanisms (except for a small detail at the definition point), but that would have been true even with a normal exception, wherever it had been defined.
Iād still advise for the use of local exceptions as good practice, but with the current compilers it is never faster than a regular exception and can be slightly slower in some cases (the definition itself is not completely free).