@effekt-lang/effekt 0.53.0 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/effekt CHANGED
Binary file
@@ -0,0 +1,169 @@
1
+ ; Value = Any
2
+ ; Generation = Int
3
+
4
+ ; Ref = Store * Value * Generation
5
+ (define-record-type ref (fields store (mutable value) (mutable generation)))
6
+
7
+ ; Node = box MEM | box Diff
8
+
9
+ ; MEM
10
+ ; Means the correct values are in MEMory
11
+ (define-record-type mem (fields))
12
+
13
+ ; Diff = Ref * Value * Generation * Node
14
+ ; Notes the DIFFerences between root and the current state in MEMory
15
+ (define-record-type diff (fields ref value generation root))
16
+
17
+ ; Store = Node * Generation
18
+ (define-record-type store (fields (mutable root) (mutable generation)))
19
+
20
+ ; Snapshot = Store * Node * Generation
21
+ (define-record-type snap (fields store root generation))
22
+
23
+ ; -> Store
24
+ (define (create-store) (make-store (box (make-mem)) 0))
25
+
26
+ ; Store -> Snapshot
27
+ (define (snapshot store)
28
+ (let* ([sGen (store-generation store)]
29
+ [snap (make-snap store (store-root store) sGen)])
30
+ (store-generation-set! store (+ sGen 1))
31
+ snap))
32
+
33
+ ; Node, [box Diff] -> [box Diff]
34
+ (define (collectDiffs n acc)
35
+ (let ([unboxedN (unbox n)])
36
+ (cond
37
+ [(mem? unboxedN) acc]
38
+ [else (collectDiffs (diff-root unboxedN) (cons n acc))])))
39
+
40
+ ; Node, [Diff] -> void
41
+ (define (applyDiffs n diffs)
42
+ (cond
43
+ [(null? diffs) (set-box! n (make-mem))]
44
+ [else
45
+ (let* ([currentDiff (car diffs)]
46
+ [realDiff (unbox currentDiff)]
47
+ [r (diff-ref realDiff)]
48
+ [oldValue (ref-value r)])
49
+ (ref-value-set! r (diff-value realDiff))
50
+ (set-box! n (make-diff r oldValue (ref-generation r) currentDiff))
51
+ (applyDiffs currentDiff (cdr diffs)))]))
52
+
53
+ ; Node, Node -> void
54
+ (define (reroot newRoot oldRoot)
55
+ (applyDiffs oldRoot (collectDiffs newRoot '())))
56
+
57
+ ; Store, Snapshot -> void
58
+ (define (restore snap)
59
+ (let* ([snapRoot (snap-root snap)]
60
+ [store (snap-store snap)]
61
+ [storeRoot (store-root store)])
62
+ (reroot snapRoot storeRoot)
63
+ (store-root-set! store snapRoot)))
64
+
65
+ ; Cont a = a, MetaCont -> #
66
+ ; Prompt = Int
67
+
68
+ ; MetaCont = Cont * Prompt * Store | Snapshot * MetaCont?
69
+ ; Holds a "copy" of k, as well as its prompt and store
70
+ (define-record-type meta-cont
71
+ (fields (mutable cont) prompt (mutable store) (mutable rest)))
72
+
73
+ ; Value, MetaCont -> Ref
74
+ (define (var init ks)
75
+ (let ([store (meta-cont-store ks)])
76
+ (make-ref store init (store-generation store))))
77
+
78
+ ; Ref -> Value
79
+ (define (get ref) (ref-value ref))
80
+
81
+ ; Ref, Value -> void
82
+ (define (put ref value)
83
+ (let* ([rGen (ref-generation ref)]
84
+ [store (ref-store ref)]
85
+ [sGen (store-generation store)])
86
+ (if (= rGen sGen)
87
+ (ref-value-set! ref value)
88
+ (let ([oldVal (ref-value ref)]
89
+ [newRoot (box (make-mem))]
90
+ [oldRoot (store-root store)])
91
+ (ref-value-set! ref value)
92
+ (ref-generation-set! ref sGen)
93
+ (set-box! oldRoot (make-diff ref oldVal rGen newRoot))
94
+ (store-root-set! store newRoot)))))
95
+
96
+ ; MetaCont -> MetaCont
97
+ (define (create-region ks) ks)
98
+
99
+ ; Value, MetaCont -> Ref
100
+ (define allocate var)
101
+
102
+ ; Ref | MetaCont -> void
103
+ (define (deallocate _) (void))
104
+
105
+ (define _prompt 1)
106
+ (define (new-prompt)
107
+ (let ([prompt _prompt])
108
+ (set! _prompt (+ prompt 1))
109
+ prompt))
110
+
111
+ (define (top-level-k x _) x)
112
+ (define top-level-ks (make-meta-cont top-level-k 0 (create-store) '()))
113
+
114
+ ; a, MetaCont -> #
115
+ (define (return x ks)
116
+ (let* ([new-ks (meta-cont-rest ks)]
117
+ [k (meta-cont-cont new-ks)])
118
+ (k x new-ks)))
119
+
120
+ ; Program a b = a, Cont b, MetaCont -> #
121
+
122
+ ; Program Prompt b, MetaCont, Cont b -> #
123
+ (define (reset prog ks k)
124
+ (let ([prompt (new-prompt)])
125
+ (meta-cont-cont-set! ks k)
126
+ (prog prompt (make-meta-cont return prompt (create-store) ks) return)))
127
+
128
+ ; MetaCont, Prompt -> MetaCont * MetaCont
129
+ (define (split-stack ks p)
130
+ (define (worker above below)
131
+ (let ([new-below (meta-cont-rest below)]
132
+ [snap (snapshot (meta-cont-store below))])
133
+ (meta-cont-store-set! below snap)
134
+ (meta-cont-rest-set! below above)
135
+ (if (= (meta-cont-prompt below) p)
136
+ (values below new-below)
137
+ (worker below new-below))))
138
+ (worker '() ks))
139
+
140
+ ; Prompt, Program MetaCont b, MetaCont, Cont b -> #
141
+ (define (shift p prog ks k)
142
+ (meta-cont-cont-set! ks k)
143
+ (let-values ([(c underC) (split-stack ks p)])
144
+ (prog c underC (meta-cont-cont underC))))
145
+
146
+ ; MetaCont, MetaCont -> MetaCont
147
+ (define (rewind cont ks)
148
+ (if (null? cont)
149
+ ks
150
+ (let* ([snap (meta-cont-store cont)]
151
+ [next (meta-cont-rest cont)]
152
+ [newKs (make-meta-cont (meta-cont-cont cont)
153
+ (meta-cont-prompt cont)
154
+ (snap-store snap)
155
+ ks)])
156
+ (restore snap)
157
+ (rewind next newKs))))
158
+
159
+ ; Block b = Cont b, MetaCont -> #
160
+
161
+ ; MetaCont, Block, MetaCont, Cont -> #
162
+ (define (resume cont block ks k)
163
+ (meta-cont-cont-set! ks k)
164
+ (let ([rewinded (rewind cont ks)])
165
+ (block rewinded (meta-cont-cont rewinded))))
166
+
167
+ ; Block b -> b
168
+ (define (run-top-level p)
169
+ (p top-level-ks top-level-k))
@@ -22,7 +22,7 @@ namespace chez {
22
22
 
23
23
  // CPS implementation
24
24
  // ------------------
25
- extern include chezLift "../chez/lift/effekt.ss"
25
+ extern include chezCPS "../chez/cps/effekt.ss"
26
26
 
27
27
  // Monadic implementation
28
28
  // ----------------------
@@ -0,0 +1,51 @@
1
+ module io/channel
2
+
3
+ import io
4
+ import io/signal
5
+
6
+ record Node[A, B](this: Signal[A, B], rest: Ref[Option[Node[A, B]]])
7
+
8
+ type Sender[A, B] = Ref[Node[A, B]]
9
+ type Receiver[A, B] = Ref[Node[A, B]]
10
+
11
+ /// Creates a new rendezvous channel and returns the receiving and the sending end.
12
+ /// Every send must be matched by exactly one receive upon which two values are exchanged.
13
+ def channel[A, B](): (Sender[A, B], Receiver[A, B]) = {
14
+ val node = Node(signal(), ref(None()))
15
+ (ref(node), ref(node))
16
+ }
17
+
18
+ /// Sends a value to a channel and blocks until a receiver arrives.
19
+ def send[A, B](sender: Sender[A, B], value: A): B = {
20
+ val Node(this, rest) = sender.get()
21
+ rest.get() match {
22
+ case None() =>
23
+ val node = Node(signal(), ref(None()))
24
+ rest.set(Some(node))
25
+ sender.set(node)
26
+ case Some(node) =>
27
+ sender.set(node)
28
+ }
29
+ // TODO avoid yield
30
+ yield()
31
+ this.unsafeFire(value)
32
+ }
33
+
34
+ /// Receives a value from a channel and blocks until a sender arrives.
35
+ def receive[A, B](receiver: Receiver[A, B], value: B): A = {
36
+ val Node(this, rest) = receiver.get()
37
+ rest.get() match {
38
+ case None() =>
39
+ val node = Node(signal(), ref(None()))
40
+ rest.set(Some(node))
41
+ receiver.set(node)
42
+ case Some(node) =>
43
+ receiver.set(node)
44
+ }
45
+ // TODO avoid yield
46
+ yield()
47
+ this.unsafeWait(value)
48
+ }
49
+
50
+ // TODO streaming
51
+
@@ -0,0 +1,53 @@
1
+ module io/promise
2
+
3
+ import io
4
+ import io/signal
5
+
6
+ // Promises
7
+ // --------
8
+
9
+ type State[T] {
10
+ Pending(signals: List[Signal[T, Unit]])
11
+ Resolved(value: T)
12
+ }
13
+
14
+ type Promise[T] = Ref[State[T]]
15
+
16
+ namespace promise {
17
+ /// Creates a pending promise that must be resolved exactly once
18
+ /// and can be awaited multiple times.
19
+ def make[T](): Promise[T] =
20
+ ref(Pending(Nil()))
21
+ }
22
+
23
+ /// Spawns a task and immediately returns a promise that upon completion
24
+ /// of the task will resolve to its result.
25
+ def promise[T](task: () => T at {io, async, global}): Promise[T] = {
26
+ val p = promise::make[T]();
27
+ spawn(box { p.resolve(task()) });
28
+ return p
29
+ }
30
+
31
+ /// Awaits and blocks until the given promise is resolved.
32
+ def await[T](promise: Promise[T]): T =
33
+ promise.get match {
34
+ case Resolved(value) =>
35
+ return value
36
+ case Pending(signals) =>
37
+ val signal = signal()
38
+ promise.set(Pending(Cons(signal, signals)))
39
+ // TODO perhaps yield to avoid stack overflow
40
+ signal.unsafeWait(())
41
+ }
42
+
43
+ /// Resolves the given promise, unblocking all awaiting tasks.
44
+ def resolve[T](promise: Promise[T], value: T): Unit =
45
+ promise.get match {
46
+ case Resolved(value) =>
47
+ panic("ERROR: Promise already resolved")
48
+ case Pending(signals) =>
49
+ promise.set(Resolved(value))
50
+ // TODO perhaps yield to avoid stack overflow
51
+ signals.reverse.foreach { signal => unsafeFire(signal, value) }
52
+ }
53
+
@@ -0,0 +1,55 @@
1
+ module io/signal
2
+
3
+ // Signals
4
+ // -------
5
+
6
+ /// Must be fired exactly once with an A receiving a B,
7
+ /// and must be waited for exactly once with a B receiving an A.
8
+ extern type Signal[A, B]
9
+
10
+ extern def signal[A, B]() at global: Signal[A, B] =
11
+ js "signal$make()"
12
+ llvm """
13
+ %signal = call %Pos @c_signal_make()
14
+ ret %Pos %signal
15
+ """
16
+
17
+ extern def unsafeFire[A, B](signal: Signal[A, B], value: A) at async: B =
18
+ js "$effekt.capture(callback => signal$notify(${signal}, ${value}, callback))"
19
+ llvm """
20
+ call void @c_signal_notify(%Pos ${signal}, %Pos ${value}, %Stack %stack)
21
+ ret void
22
+ """
23
+
24
+ extern def unsafeWait[A, B](signal: Signal[A, B], value: B) at async: A =
25
+ js "$effekt.capture(callback => signal$notify(${signal}, ${value}, callback))"
26
+ llvm """
27
+ call void @c_signal_notify(%Pos ${signal}, %Pos ${value}, %Stack %stack)
28
+ ret void
29
+ """
30
+
31
+ extern llvm """
32
+ declare %Pos @c_signal_make()
33
+ declare void @c_signal_notify(%Pos, %Pos, %Stack)
34
+ """
35
+
36
+ extern js """
37
+ function signal$make() {
38
+ return { state: 0, value: null, stack: null };
39
+ }
40
+ function signal$notify(signal, value, stack) {
41
+ if (signal.state === 0) {
42
+ signal.state = 1;
43
+ signal.value = value;
44
+ signal.stack = stack;
45
+ } else {
46
+ const otherValue = signal.value;
47
+ const otherStack = signal.stack;
48
+ signal.value = null;
49
+ signal.stack = null;
50
+
51
+ otherStack(value);
52
+ stack(otherValue);
53
+ }
54
+ }
55
+ """
@@ -4,7 +4,7 @@ extern llvm """
4
4
  declare void @c_timer_start(%Int, %Stack)
5
5
  """
6
6
 
7
- extern def wait(millis: Int) at async: Unit =
7
+ extern def sleep(millis: Int) at async: Unit =
8
8
  js "$effekt.capture(k => setTimeout(() => k($effekt.unit), ${millis}))"
9
9
  llvm """
10
10
  call void @c_timer_start(%Int ${millis}, %Stack %stack)
@@ -1,18 +1,14 @@
1
1
  module io
2
2
 
3
- import ref
4
- import queue
5
3
 
6
4
  // Event Loop
7
5
  // ----------
8
6
 
9
- type Task[T] = () => T at {io, async, global}
10
-
11
7
  extern llvm """
12
8
  declare void @c_yield(%Stack)
13
9
  """
14
10
 
15
- extern def spawn(task: Task[Unit]) at async: Unit =
11
+ extern def spawn[T](task: () => T at {io, async, global}) at async: Unit =
16
12
  js "$effekt.capture(k => { setTimeout(() => k($effekt.unit), 0); return $effekt.run(${task}) })"
17
13
  llvm """
18
14
  call void @c_yield(%Stack %stack)
@@ -34,55 +30,3 @@ extern def abort() at async: Nothing =
34
30
  ret void
35
31
  """
36
32
 
37
-
38
- // Promises
39
- // --------
40
-
41
- extern type Promise[T]
42
- // = js "{resolve: ƒ, promise: Promise}"
43
- // = llvm "{tag: 0, obj: Promise*}"
44
-
45
- def promise[T](task: Task[T]): Promise[T] = {
46
- val p = promise::make[T]();
47
- spawn(box { p.resolve(task()) });
48
- return p
49
- }
50
-
51
- extern llvm """
52
- declare %Pos @c_promise_make()
53
- declare void @c_promise_resolve(%Pos, %Pos)
54
- declare void @c_promise_await(%Pos, %Neg)
55
- """
56
-
57
- extern def await[T](promise: Promise[T]) at async: T =
58
- js "$effekt.capture(k => ${promise}.promise.then(k))"
59
- llvm """
60
- call void @c_promise_await(%Pos ${promise}, %Stack %stack)
61
- ret void
62
- """
63
-
64
- extern def resolve[T](promise: Promise[T], value: T) at async: Unit =
65
- js "$effekt.capture(k => { ${promise}.resolve(${value}); return k($effekt.unit) })"
66
- llvm """
67
- call void @c_promise_resolve(%Pos ${promise}, %Pos ${value}, %Stack %stack)
68
- ret void
69
- """
70
-
71
- namespace promise {
72
- extern js """
73
- function promise$make() {
74
- let resolve;
75
- const promise = new Promise((res, rej) => {
76
- resolve = res;
77
- });
78
- return { resolve: resolve, promise: promise };
79
- }
80
- """
81
-
82
- extern def make[T]() at io: Promise[T] =
83
- js "promise$make()"
84
- llvm """
85
- %promise = call %Pos @c_promise_make()
86
- ret %Pos %promise
87
- """
88
- }
@@ -944,6 +944,22 @@ def each[A](list: List[A]): Unit / emit[A] =
944
944
  each(tail)
945
945
  }
946
946
 
947
+ /// Push iterator that emits all the tails (suffixes) of the given `list`.
948
+ ///
949
+ /// Examples:
950
+ /// ```effekt
951
+ /// > list::collect[List[Int]] { [1, 2, 3].tails }
952
+ /// [[1, 2, 3], [2, 3], [3], []]
953
+ ///
954
+ /// > list::collect[List[Int]] { [].tails }
955
+ /// [[]]
956
+ /// ```
957
+ def tails[A](list: List[A]): Unit / emit[List[A]] =
958
+ list match {
959
+ case Nil() => do emit(Nil())
960
+ case Cons(head, tail) => do emit(list); tails(tail)
961
+ }
962
+
947
963
  def feed[T, R](list: List[T]) { reader: () => R / read[T] }: R = {
948
964
  var l = list
949
965
  try {
@@ -1,4 +1,5 @@
1
1
  module process
2
+ import array
2
3
 
3
4
  extern def exit(errorCode: Int) at io: Nothing =
4
5
  js "(function() { process.exit(${errorCode}) })()"
@@ -7,3 +8,101 @@ extern def exit(errorCode: Int) at io: Nothing =
7
8
  ret %Pos zeroinitializer
8
9
  """
9
10
  chez "(exit ${errorCode})"
11
+
12
+ // Subprocesses
13
+ // ------------
14
+
15
+ extern jsNode """
16
+ const { spawn } = require('node:child_process');
17
+ """
18
+ extern llvm """
19
+ declare %Pos @c_spawn_options_default()
20
+ declare %Pos @c_spawn_options_on_stdout(%Pos, %Pos)
21
+ declare %Pos @c_spawn_options_on_stderr(%Pos, %Pos)
22
+ declare %Pos @c_spawn_options_pipe_stdin(%Pos, %Pos)
23
+ declare %Pos @c_write_stream(%Pos, %Pos)
24
+ declare %Pos @c_close_stream(%Pos)
25
+ declare %Pos @c_spawn(%Pos, %Pos, %Pos)
26
+ """
27
+
28
+ extern type SpawnOptions
29
+ extern def default() at {}: SpawnOptions =
30
+ jsNode """{ stdio: [0,1,2], onSpawn: (p) => {} }"""
31
+ llvm """
32
+ %opts = call %Pos @c_spawn_options_default()
33
+ ret %Pos %opts
34
+ """
35
+ extern def onStdout(opts: SpawnOptions, callback: String => Unit at {io, global}) at {}: SpawnOptions =
36
+ jsNode """(function() {
37
+ let old = ${opts};
38
+ old.stdio[1] = 'pipe';
39
+ let oldSpawn = old.onSpawn;
40
+ old.onSpawn = function(p) {
41
+ oldSpawn(p);
42
+ p.stdout.on('data', (chunk) => {
43
+ $effekt.runToplevel((ks, k) => (${callback})(chunk, ks, k));
44
+ });
45
+ };
46
+ return old; })()"""
47
+ llvm """
48
+ %opts = call %Pos @c_spawn_options_on_stdout(%Pos ${opts}, %Pos ${callback})
49
+ ret %Pos %opts
50
+ """
51
+ extern def onStderr(opts: SpawnOptions, callback: String => Unit at {io, global}) at {}: SpawnOptions =
52
+ jsNode """(function() {
53
+ let old = ${opts};
54
+ old.stdio[2] = 'pipe';
55
+ let oldSpawn = old.onSpawn;
56
+ old.onSpawn = function(p) {
57
+ oldSpawn(p);
58
+ p.stderr.on('data', (chunk) => {
59
+ $effekt.runToplevel((ks, k) => (${callback})(chunk, ks, k));
60
+ });
61
+ };
62
+ return old; })()"""
63
+ llvm """
64
+ %opts = call %Pos @c_spawn_options_on_stderr(%Pos ${opts}, %Pos ${callback})
65
+ ret %Pos %opts
66
+ """
67
+ extern type WritableStream
68
+ extern def withStdin(opts: SpawnOptions, callback: WritableStream => Unit at {io, global}) at {}: SpawnOptions =
69
+ jsNode """(function () {
70
+ let old = ${opts};
71
+ old.stdio[0] = 'pipe'
72
+ let oldSpawn = old.onSpawn;
73
+ old.onSpawn = function(p) {
74
+ oldSpawn(p);
75
+ p.on('spawn', () => {
76
+ $effekt.runToplevel((ks, k) => (${callback})(p.stdin, ks, k));
77
+ });
78
+ };
79
+ return old;
80
+ })()"""
81
+ llvm """
82
+ %opts = call %Pos @c_spawn_options_pipe_stdin(%Pos ${opts}, %Pos ${callback})
83
+ ret %Pos %opts
84
+ """
85
+ extern def write(to: WritableStream, s: String) at io: Unit =
86
+ jsNode """${to}.write(${s})"""
87
+ llvm """
88
+ call void @c_write_stream(%Pos ${to}, %Pos ${s})
89
+ ret %Pos zeroinitializer
90
+ """
91
+ extern def close(to: WritableStream) at io: Unit =
92
+ jsNode """${to}.end()"""
93
+ llvm """
94
+ call void @c_close_stream(%Pos ${to})
95
+ ret %Pos zeroinitializer
96
+ """
97
+
98
+ extern def spawn(cmd: String, args: Array[String], options: SpawnOptions) at async: Int =
99
+ jsNode """$effekt.capture(k => {
100
+ let p = spawn(${cmd}, ${args}, ${options});
101
+ ${options}.onSpawn(p);
102
+ p.on('close', (exitcode, sig) => { k(exitcode) });
103
+ return p;
104
+ })"""
105
+ llvm """
106
+ call void @c_spawn(%Pos ${cmd}, %Pos ${args}, %Pos ${options}, %Stack %stack)
107
+ ret void
108
+ """