@@ -24,7 +24,7 @@ To understand the basics, let us define an effect (that is, an operation) that
24
24
takes an integer argument and returns an integer result. We name this effect
25
25
"Xchg".
26
26
27
- \begin{caml_example*}{verbatim}
27
+ \begin{caml_example*}{verbatim}[error]
28
28
open Effect
29
29
open Effect.Deep
30
30
@@ -42,7 +42,7 @@ returns their sum.
42
42
We can handle the "Xchg" effect by implementing a handler that always returns
43
43
the successor of the offered value:
44
44
45
- \begin{caml_example}{verbatim}
45
+ \begin{caml_example}{verbatim}[error]
46
46
try_with comp1 ()
47
47
{ effc = fun (type a) (eff: a t) ->
48
48
match eff with
@@ -99,7 +99,7 @@ computations \textit{tasks}.
99
99
A task either is in a suspended state or is completed. We represent the task
100
100
status as follows:
101
101
102
- \begin{caml_example*}{verbatim}
102
+ \begin{caml_example*}{verbatim}[error]
103
103
type 'a status =
104
104
Complete of 'a
105
105
| Suspended of {msg: int; cont: (int, 'a status) continuation}
@@ -113,7 +113,7 @@ resume and returns a "'a status" value when resumed.
113
113
Next, we define a "step" function that executes one step of computation until
114
114
it completes or suspends:
115
115
116
- \begin{caml_example*}{verbatim}
116
+ \begin{caml_example*}{verbatim}[error]
117
117
let step (f : unit -> 'a) () : 'a status =
118
118
match_with f ()
119
119
{ retc = (fun v -> Complete v);
@@ -156,7 +156,7 @@ the status does not perform the "Xchg" effect.
156
156
157
157
We can now write a simple scheduler that runs a pair of tasks to completion:
158
158
159
- \begin{caml_example*}{verbatim}
159
+ \begin{caml_example*}{verbatim}[error]
160
160
let rec run_both a b =
161
161
match a (), b () with
162
162
| Complete va, Complete vb -> (va, vb)
@@ -175,13 +175,13 @@ the handler to raise an exception
175
175
176
176
We can now define a second computation that also exchanges two messages:
177
177
178
- \begin{caml_example*}{verbatim}
178
+ \begin{caml_example*}{verbatim}[error]
179
179
let comp2 () = perform (Xchg 21) * perform (Xchg 21)
180
180
\end{caml_example*}
181
181
182
182
Finally, we can run the two computations together:
183
183
184
- \begin{caml_example}{verbatim}
184
+ \begin{caml_example}{verbatim}[error]
185
185
run_both (step comp1) (step comp2)
186
186
\end{caml_example}
187
187
@@ -202,7 +202,7 @@ user-level threading systems provide a "fork" primitive to spawn off a new
202
202
concurrent task and a "yield" primitive to yield control to some other task.
203
203
Correspondingly, we shall declare two effects as follows:
204
204
205
- \begin{caml_example*}{verbatim}
205
+ \begin{caml_example*}{verbatim}[error]
206
206
type _ Effect.t += Fork : (unit -> unit) -> unit t
207
207
| Yield : unit t
208
208
\end{caml_example*}
@@ -215,15 +215,15 @@ also offering to exchange a value.
215
215
216
216
We shall also define helper functions that simply perform these effects:
217
217
218
- \begin{caml_example*}{verbatim}
218
+ \begin{caml_example*}{verbatim}[error]
219
219
let fork f = perform (Fork f)
220
220
let yield () = perform Yield
221
221
let xchg v = perform (Xchg v)
222
222
\end{caml_example*}
223
223
224
224
A top-level "run" function defines the scheduler:
225
225
226
- \begin{caml_example*}{verbatim}
226
+ \begin{caml_example*}{verbatim}[error]
227
227
(* A concurrent round-robin scheduler *)
228
228
let run (main : unit -> unit) : unit =
229
229
let exchanger = ref None in (* waiting exchanger *)
@@ -296,7 +296,7 @@ explain and fix this in the next section~\ref{s:effects-discontinue}.
296
296
Now we can write a concurrent program that utilises the newly defined
297
297
operations:
298
298
299
- \begin{caml_example}{verbatim}
299
+ \begin{caml_example}{verbatim}[error]
300
300
open Printf
301
301
302
302
let _ = run (fun _ ->
@@ -341,7 +341,7 @@ exchange a value (stored in the reference cell "exchanger"), which remains
341
341
blocked forever! If the blocked task holds onto resources, these resources are
342
342
leaked. For example, consider the following task:
343
343
344
- \begin{caml_example*}{verbatim}
344
+ \begin{caml_example*}{verbatim}[error]
345
345
let leaky_task () =
346
346
fork (fun _ ->
347
347
let oc = open_out "secret.txt" in
@@ -442,7 +442,7 @@ lst_iter (fun i -> Printf.printf "%d\n" i)
442
442
The expression "invert lst_iter" returns a sequence that allows the consumer to
443
443
traverse the list on demand. For example,
444
444
445
- \begin{caml_example}{verbatim}
445
+ \begin{caml_example}{verbatim}[error]
446
446
let s = invert ~iter:lst_iter
447
447
let next = Seq.to_dispenser s;;
448
448
next();;
@@ -453,7 +453,7 @@ next();;
453
453
454
454
We can use the same "invert" function on any "iter" function. For example,
455
455
456
- \begin{caml_example}{verbatim}
456
+ \begin{caml_example}{verbatim}[error]
457
457
let s = invert ~iter:(Fun.flip String.iter "OCaml")
458
458
let next = Seq.to_dispenser s;;
459
459
next();;
@@ -468,7 +468,7 @@ next();;
468
468
469
469
The implementation of the "invert" function is given below:
470
470
471
- \begin{caml_example*}{verbatim}
471
+ \begin{caml_example*}{verbatim}[error]
472
472
let invert (type a) ~(iter : (a -> unit) -> unit) : a Seq.t =
473
473
let module M = struct
474
474
type _ Effect.t += Yield : a -> unit t
@@ -517,7 +517,7 @@ examples.
517
517
518
518
Like exception handlers, effect handlers can be nested.
519
519
520
- \begin{caml_example*}{verbatim}
520
+ \begin{caml_example*}{verbatim}[error]
521
521
type _ Effect.t += E : int t
522
522
| F : string t
523
523
@@ -544,7 +544,7 @@ In this example, the computation "foo" performs "F", the inner handler handles
544
544
only "E" and the outer handler handles "F". The call to "baz" returns "Hello,
545
545
world!".
546
546
547
- \begin{caml_example}{verbatim}
547
+ \begin{caml_example}{verbatim}[error]
548
548
baz ()
549
549
\end{caml_example}
550
550
@@ -606,7 +606,7 @@ corresponding "perform". For example, in the previous example, "bar" does not
606
606
handle the effect "F". Hence, we will get an "Effect.Unhandled F" exception
607
607
when we run "bar".
608
608
609
- \begin{caml_example}{verbatim}
609
+ \begin{caml_example}{verbatim}[error]
610
610
try bar () with Effect.Unhandled F -> "Saw Effect.Unhandled exception"
611
611
\end{caml_example}
612
612
@@ -618,7 +618,7 @@ must be resumed either with a "continue" or "discontinue" exactly once}}.
618
618
Attempting to use a continuation more than once raises a
619
619
"Continuation_already_resumed" exception. For example:
620
620
621
- \begin{caml_example}{verbatim}
621
+ \begin{caml_example}{verbatim}[error]
622
622
try_with perform (Xchg 0)
623
623
{ effc = fun (type a) (eff : a t) ->
624
624
match eff with
@@ -685,7 +685,7 @@ implement a shallow handler that enforces a particular sequence of effects (a
685
685
protocol) on a computation. For this example, let us consider that the
686
686
computation may perform the following effects:
687
687
688
- \begin{caml_example*}{verbatim}
688
+ \begin{caml_example*}{verbatim}[error]
689
689
type _ Effect.t += Send : int -> unit Effect.t
690
690
| Recv : int Effect.t
691
691
\end{caml_example*}
@@ -698,7 +698,7 @@ not "[Recv]", "[Send;Send]", "[Send;Recv;Recv]", etc. The key observation here
698
698
is that the set of effects handled evolves over time. We can enforce this
699
699
protocol quite naturally using shallow handlers as shown below:
700
700
701
- \begin{caml_example*}{verbatim}
701
+ \begin{caml_example*}{verbatim}[error]
702
702
open Effect.Shallow
703
703
704
704
let run (comp: unit -> unit) : unit =
@@ -753,7 +753,7 @@ be handled by an outer handler.
753
753
We can see that the "run" function will permit a computation that follows the
754
754
protocol:
755
755
756
- \begin{caml_example}{verbatim}
756
+ \begin{caml_example}{verbatim}[error]
757
757
run (fun () ->
758
758
printf "Send 42\n";
759
759
perform (Send 42);
@@ -765,7 +765,7 @@ run (fun () ->
765
765
766
766
and aborts those that do not:
767
767
768
- \begin{caml_example}{verbatim}
768
+ \begin{caml_example}{verbatim}[error]
769
769
run (fun () ->
770
770
Printf.printf "Send 0\n";
771
771
perform (Send 0);
0 commit comments