File: shallow2deep.ml

package info (click to toggle)
js-of-ocaml 6.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 37,932 kB
  • sloc: ml: 135,957; javascript: 58,364; ansic: 437; makefile: 422; sh: 12; perl: 4
file content (110 lines) | stat: -rw-r--r-- 2,669 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
(* TEST *)

open Printf
open Effect
open Effect.Deep

(* Francois Pottier's implementation of shallow handlers on top of
   deep handlers, by reification of an effectful operation
   as a stream of events. *)

module MkReify
  (X : sig
     (* A type of operations ['a op]. *)
     type 'a op
     (* An effect name [E]. *)
     type _ eff += E : 'a op -> 'a eff
  end)
= struct
  open Effect
  open Effect.Deep
  open X

  (* The type ['a event] represents a computation whose result type is ['a].
     It can be thought of as a lazy sequence of events, where an event is
     either normal termination [Ret] or an effect [Eff]. The first event of
     the stream is immediately available for inspection; the rest of the
     computation is suspended and represented as a continuation. *)

  type 'a event =
  | Ret : 'a -> 'a event
  | Eff : 'a op * ('a, 'b event) continuation -> 'b event

  (* [reify] transforms an effectful computation into a stream of events.
     The effects named [E] are caught and become events in the stream. *)

  let reify (type a) (m : unit -> a) : a event =
    match m () with
    | x -> Ret x
    | effect E op, k -> Eff(op, k)

end

module PC = struct

  type data = int

  type _ op =
    | Yield : data -> unit op
    | Await : data op

  type _ eff += E : 'a op -> 'a eff

end

open PC

let yield x =
  perform (E (Yield x))

let await () =
  perform (E Await)

exception ProducerPushedTooFar (* This helps us test. *)

let zero_producer () =
  raise ProducerPushedTooFar

let zero_consumer () =
  "I need no data."

let test_producer () =
  yield 1;
  yield 2;
  raise ProducerPushedTooFar

let test_consumer () =
  let x = await() in
  let y = await() in
  Printf.sprintf "I have received %d and %d." x y

open MkReify(PC)

let rec run_consumer (p : unit -> unit event) (c : 'c event) : 'c =
  match c with
  | Ret x ->
      x
  | Eff (Await, k) ->
      let c : data -> 'c event = continue k in
      run_producer p c
  | Eff (Yield _, _) ->
      assert false (* consumer must not yield *)

and run_producer (p : unit -> unit event) (c : data -> 'c event) : 'c =
  match p() with
  | Ret () ->
      assert false (* producer must not stop early *)
  | Eff (Yield data, k) ->
      run_consumer (continue k) (c data)
  | Eff (Await, _) ->
      assert false (* producer must not await *)

let pipe (type c) (p : unit -> unit) (c : unit -> c) : c =
  run_consumer (fun () -> reify p) (reify c)

let _ =
  printf "%s\n" (pipe test_producer test_consumer);
  printf "%s\n" (pipe zero_producer zero_consumer);
  printf "%s\n"
    (try pipe zero_producer test_consumer
     with ProducerPushedTooFar -> "Producer pushed too far.")