Hi there,
You can actually implement a call/cc-like behavior from @octachron’s first construct :
let callcc (type a) f =
let exception Kont of a in
let k x = raise (Kont x) in
try f k with
| Kont x -> x
val callcc : (('a -> 'b) -> 'a) -> 'a = <fun>
callcc
(fun k ->
let n = 1 + k 50 in
(* never reaches here *)
n * n
);;
- : int = 50
However, you need to be careful not to accidentally sink the exception from within the inner function :
let f k =
try
(* code that may trigger a Not_found exception *)
k 50
with
| _ -> 12 ;;
callcc f
- : int = 12
Let’s suppose that no Not_found exception was raised at line 3.
The exception (Kont 50) fires at line 4 and the deepest "try … with … " handler first gets the chance to match it.
Since the wildcard “_” matches any exception, (Kont 50) gets replaced by the value 12 and callcc is left under the impression that no exception has ever occured !
That’s why you should restrict that wildcard use and be more specific when catching an exception :
let f k =
try
(* code that may trigger a Not_found exception *)
k 50
with
| Not_found -> 12 ;;
callcc f;;
- : int = 50
Here, the exception (Kont 50) is not matched by the local handler.
Callcc’s handler is next in line to process it : there is a match and (Kont 50) is then caught and eventually replaced with 50
I do avoid handling exceptions whenever possible. In most usecases, you can use the “result monad” instead, a nice pattern that both composes well and yields more readable code :
exception vs. monad on Ocaml discuss
result monad in action
Nota :
If f needs additional arguments, you can slightly change the definition of callcc :
let callcc1 (type a) f arg =
let exception Kont of a in
let k x = raise (Kont x) in
try f k arg with
| Kont x -> x
val callcc1 : (('a -> 'b) -> 'c -> 'a) -> 'c -> 'a = <fun>