@effekt-lang/effekt 0.54.0 → 0.56.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
@@ -70,7 +70,7 @@
70
70
  (cond
71
71
  ; Same object reference (fast path)
72
72
  [(eq? obj1 obj2) #t]
73
-
73
+
74
74
  ; If both are records, compare them structurally
75
75
  [(and (record? obj1) (record? obj2))
76
76
  (let* ([rtd1 (record-rtd obj1)]
@@ -87,7 +87,7 @@
87
87
  (if (not (equal_impl field1 field2))
88
88
  (set! result #f)))))
89
89
  #f))]
90
-
90
+
91
91
  ; For lists, compare elements recursively
92
92
  [(and (list? obj1) (list? obj2))
93
93
  (and (= (length obj1) (length obj2))
@@ -95,7 +95,7 @@
95
95
  (or (null? l1)
96
96
  (and (equal_impl (car l1) (car l2))
97
97
  (loop (cdr l1) (cdr l2))))))]
98
-
98
+
99
99
  ; For all other types, use Scheme's built-in equal?
100
100
  [else (equal? obj1 obj2)]))
101
101
 
@@ -134,4 +134,10 @@
134
134
  (raise
135
135
  (condition
136
136
  (make-error)
137
- (make-message-condition (string-append pos " not implemented yet" )))))
137
+ (make-message-condition (string-append pos " not implemented yet" )))))
138
+
139
+ (define (unreachable)
140
+ (raise
141
+ (condition
142
+ (make-error)
143
+ (make-message-condition ("should not be reachable!")))))
@@ -0,0 +1,133 @@
1
+ module dictionary
2
+
3
+ import array
4
+ import bytearray
5
+ import stream
6
+
7
+
8
+ /// A trie-based dictionary for efficient key-value storage with bytearray keys.
9
+ /// Supports String and ByteArray keys with fast lookup and insertion.
10
+ type Dictionary[A] {
11
+ Empty()
12
+ Leaf(value: A)
13
+ Branch(children: Array[Dictionary[A]])
14
+ Node(value: A, children: Array[Dictionary[A]])
15
+ }
16
+
17
+ /// Insert a value with a String key.
18
+ def insert[A](trie: Dictionary[A], key: String, value: A): Dictionary[A] =
19
+ insert(trie, key.fromString, value)
20
+
21
+ /// Insert a key-value pair into the dictionary with a ByteArray key.
22
+ def insert[A](trie: Dictionary[A], key: ByteArray, value: A): Dictionary[A] = {
23
+ def go(trie: Dictionary[A], i: Int): Dictionary[A] =
24
+ if (i < key.size) {
25
+ trie match {
26
+ case Empty() =>
27
+ val child = go(Empty(), i + 1)
28
+ val children = array(256, Empty())
29
+ children.unsafeSet(key.unsafeGet(i).toInt, child)
30
+ Branch(children)
31
+ case Leaf(existing) =>
32
+ val child = go(Empty(), i + 1)
33
+ val children = array(256, Empty())
34
+ children.unsafeSet(key.unsafeGet(i).toInt, child)
35
+ Node(existing, children)
36
+ case Branch(children) =>
37
+ val child = go(children.unsafeGet(key.unsafeGet(i).toInt), i + 1)
38
+ children.unsafeSet(key.unsafeGet(i).toInt, child)
39
+ Branch(children)
40
+ case Node(existing, children) =>
41
+ val child = go(children.unsafeGet(key.unsafeGet(i).toInt), i + 1)
42
+ children.unsafeSet(key.unsafeGet(i).toInt, child)
43
+ Node(existing, children)
44
+ }
45
+ } else {
46
+ trie match {
47
+ case Empty() => Leaf(value)
48
+ case Leaf(_) => Leaf(value)
49
+ case Node(_, children) => Node(value, children)
50
+ case Branch(children) => Node(value, children)
51
+ }
52
+ }
53
+ go(trie, 0)
54
+ }
55
+
56
+ /// Look up a value in the dictionary by string key.
57
+ def lookup[A](trie: Dictionary[A], key: String): Option[A] =
58
+ lookup(trie, key.fromString)
59
+
60
+ /// Look up a value in the dictionary by bytearray key.
61
+ def lookup[A](trie: Dictionary[A], key: ByteArray): Option[A] = {
62
+ def go(trie: Dictionary[A], i: Int): Option[A] =
63
+ if (i < key.size) {
64
+ trie match {
65
+ case Empty() => None()
66
+ case Leaf(_) => None()
67
+ case Branch(children) => go(children.unsafeGet(key.unsafeGet(i).toInt), i + 1)
68
+ case Node(_, children) => go(children.unsafeGet(key.unsafeGet(i).toInt), i + 1)
69
+ }
70
+ } else {
71
+ trie match {
72
+ case Empty() => None()
73
+ case Leaf(value) => Some(value)
74
+ case Branch(_) => None()
75
+ case Node(value, _) => Some(value)
76
+ }
77
+ }
78
+ go(trie, 0)
79
+ }
80
+
81
+ /// A record pairing a key with a value for use in streams.
82
+ record Tagged[A](key: ByteArray, value: A)
83
+
84
+ /// Collect key-value pairs from a stream of Tagged elements into a Dictionary.
85
+ def collect[A] { stream: () => Unit / emit[Tagged[A]] }: Dictionary[A] = {
86
+ var trie = Empty()
87
+ for[Tagged[A]] { stream() } { case Tagged(bytes, value) =>
88
+ trie = trie.insert(bytes, value)
89
+ }
90
+ return trie
91
+ }
92
+
93
+ /// Emit all values stored in the dictionary as a push stream.
94
+ def each[A](trie: Dictionary[A]): Unit / emit[A] =
95
+ trie match {
96
+ case Empty() => ()
97
+ case Leaf(value) => do emit(value)
98
+ case Branch(children) =>
99
+ each(0, children.size) { i =>
100
+ each(children.unsafeGet(i))
101
+ }
102
+ case Node(value, children) =>
103
+ do emit(value)
104
+ each(0, children.size) { i =>
105
+ each(children.unsafeGet(i))
106
+ }
107
+ }
108
+
109
+ /// Emit all key-value pairs stored in the dictionary as a push stream of Tagged elements.
110
+ def eachTagged[A](trie: Dictionary[A]): Unit / emit[Tagged[A]] = {
111
+ def fromBytesReverse(bytes: List[Byte], n: Int): ByteArray = {
112
+ val key = bytearray::allocate(n)
113
+ foreachIndex(bytes) { (i, b) => key.unsafeSet(n - 1 - i, b) }
114
+ key
115
+ }
116
+ def go(trie: Dictionary[A], key: List[Byte], n: Int): Unit =
117
+ trie match {
118
+ case Empty() => ()
119
+ case Leaf(value) => do emit(Tagged(fromBytesReverse(key, n), value))
120
+ case Branch(children) =>
121
+ each(0, children.size) { i =>
122
+ go(children.unsafeGet(i), Cons(i.toByte, key), n + 1)
123
+ }
124
+ case Node(value, children) =>
125
+ do emit(Tagged(fromBytesReverse(key, n), value))
126
+ each(0, children.size) { i =>
127
+ go(children.unsafeGet(i), Cons(i.toByte, key), n + 1)
128
+ }
129
+ }
130
+ go(trie, Nil(), 0)
131
+ }
132
+
133
+
@@ -25,12 +25,12 @@ def handleJsonBuilder[R]{b: JsonBuilder}{ body: => R / JsonBuilder }: R = try bo
25
25
  def bool(v) = resume(b.bool(v))
26
26
  def number(n) = resume(b.number(n))
27
27
  def string(s) = resume(b.string(s))
28
- def list() = resume { {c} => b.list{c} }
29
- def dict() = resume { {c} => b.dict{c} }
28
+ def list[T]() = resume { {c} => b.list{c} }
29
+ def dict[T]() = resume { {c} => b.dict{c} }
30
30
  }
31
31
  /// Make explicitly bound JsonObjectBuilder instance implicit
32
32
  def handleJsonObjectBuilder[R]{b: JsonObjectBuilder}{ body: => R / JsonObjectBuilder }: R = try body() with JsonObjectBuilder {
33
- def field(k) = resume { {c} => b.field(k){c} }
33
+ def field[T](k) = resume { {c} => b.field(k){c} }
34
34
  }
35
35
 
36
36
  // --------------------------------------------------------------------------------
@@ -49,8 +49,8 @@ def encodeJsonList[R]{ body: => R / JsonBuilder }: R / emit[String] = {
49
49
  def bool(v) = { c(); resume(do bool(v)) }
50
50
  def number(n) = { c(); resume(do number(n)) }
51
51
  def string(s) = { c(); resume(do string(s)) }
52
- def list() = resume { { b } => c(); do list{b} }
53
- def dict() = resume { { b } => c(); do dict{b} }
52
+ def list[T]() = resume { { b } => c(); do list{b} }
53
+ def dict[T]() = resume { { b } => c(); do dict{b} }
54
54
  }
55
55
  }
56
56
  do emit("]")
@@ -66,7 +66,7 @@ def encodeJsonObject[R]{ body: => R / JsonObjectBuilder }: R / emit[String] = {
66
66
  }
67
67
  val r = encodeJson {
68
68
  try body() with JsonObjectBuilder {
69
- def field(k) = resume { {b} => c(k); b() }
69
+ def field[T](k) = resume { {b} => c(k); b() }
70
70
  }
71
71
  }
72
72
  do emit("}")
@@ -80,8 +80,8 @@ def encodeJson[R]{ body: => R / JsonBuilder }: R / emit[String] = {
80
80
  def bool(v) = { resume(do emit( if(v){ "true" } else { "false" } )) }
81
81
  def number(n) = { resume(do emit(show(n))) }
82
82
  def string(s) = { resume(do emit(escape(s))) }
83
- def list() = resume { {b} => encodeJsonList { b() } }
84
- def dict() = resume { {b} => encodeJsonObject { b() } }
83
+ def list[T]() = resume { {b} => encodeJsonList { b() } }
84
+ def dict[T]() = resume { {b} => encodeJsonObject { b() } }
85
85
  }
86
86
  }
87
87
 
@@ -209,7 +209,7 @@ def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} =
209
209
  // --------------------------------------------------------------------------------
210
210
  /// Ignore the generated calls to JsonObjectBuilder
211
211
  def ignoreDict[R](){ body: => R / JsonObjectBuilder }: R = try body() with JsonObjectBuilder {
212
- def field(k) = resume { {v} => ignore{v} }
212
+ def field[T](k) = resume { {v} => ignore{v} }
213
213
  }
214
214
 
215
215
  /// Ignore the generated calls to JsonBuilder
@@ -218,8 +218,8 @@ def ignore[R](){ body: => R / JsonBuilder }: R = try body() with JsonBuilder {
218
218
  def bool(b) = resume(())
219
219
  def null() = resume(())
220
220
  def string(s) = resume(())
221
- def list() = resume { {e} => ignore{e} }
222
- def dict() = resume { {e} => ignoreDict{e} }
221
+ def list[T]() = resume { {e} => ignore{e} }
222
+ def dict[T]() = resume { {e} => ignoreDict{e} }
223
223
  }
224
224
 
225
225
  // --------------------------------------------------------------------------------
@@ -264,12 +264,12 @@ def build[R](){ body: => R / JsonBuilder }: (R, JsonValue) = {
264
264
  def bool(b) = { r = Bool(b); resume(()) }
265
265
  def null() = { r = Null(); resume(()) }
266
266
  def string(s) = { r = String(s); resume(()) }
267
- def list() = resume { {els} =>
267
+ def list[T]() = resume { {els} =>
268
268
  val x = buildList {els}
269
269
  r = List(x.second)
270
270
  x.first
271
271
  }
272
- def dict() = resume { {fs} =>
272
+ def dict[T]() = resume { {fs} =>
273
273
  val x = buildDict {fs}
274
274
  r = Dict(x.second)
275
275
  x.first
@@ -283,12 +283,12 @@ def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = list::ret
283
283
  def bool(b) = { do emit(Bool(b)); resume(()) }
284
284
  def null() = { do emit(Null()); resume(()) }
285
285
  def string(s) = { do emit(String(s)); resume(()) }
286
- def list() = resume { {els} =>
286
+ def list[T]() = resume { {els} =>
287
287
  val x = buildList {els}
288
288
  do emit(List(x.second))
289
289
  x.first
290
290
  }
291
- def dict() = resume { {fs} =>
291
+ def dict[T]() = resume { {fs} =>
292
292
  val x = buildDict {fs}
293
293
  do emit(Dict(x.second))
294
294
  x.first
@@ -297,7 +297,7 @@ def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = list::ret
297
297
  }
298
298
  def buildDict[R](){ body: => R / JsonObjectBuilder }: (R, List[(String, JsonValue)]) = list::returning::collect[(String, JsonValue), R] {
299
299
  try body() with JsonObjectBuilder {
300
- def field(k) = resume { {v} =>
300
+ def field[T](k) = resume { {v} =>
301
301
  val x = build{v}
302
302
  do emit((k, x.second))
303
303
  x.first
@@ -325,7 +325,7 @@ namespace test {
325
325
  with scanner[Char]
326
326
 
327
327
  def k = new JsonObjectBuilder {
328
- def field(k){v} = { println(k); ignore{v} }
328
+ def field[T](k){v} = { println(k); ignore{v} }
329
329
  }
330
330
  def jsonBuilder[R] {e: => R / JsonBuilder}: R = {
331
331
  def d = new JsonBuilder {
@@ -333,8 +333,8 @@ namespace test {
333
333
  def bool(b) = ()
334
334
  def null() = println("NULL")
335
335
  def string(s) = ()
336
- def list(){e} = jsonBuilder{e}
337
- def dict(){e} = handleJsonObjectBuilder{k}{e}
336
+ def list[T](){e} = jsonBuilder{e}
337
+ def dict[T](){e} = handleJsonObjectBuilder{k}{e}
338
338
  }
339
339
  handleJsonBuilder{d}{e}
340
340
  }
@@ -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 {
@@ -37,7 +37,7 @@ def foreach[A](self: Option[A]) { f: A => Unit }: Unit = self match {
37
37
  }
38
38
 
39
39
  def value[A](self: Option[A]): A / Exception[MissingValue] = self match {
40
- case None() => do raise(MissingValue(), "Trying to get the value of an empty option")
40
+ case None() => do raise(MissingValue(), "Trying to get the value of an empty option") match {}
41
41
  case Some(v) => v
42
42
  }
43
43
 
@@ -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
+ """
@@ -170,7 +170,7 @@ def main() = {
170
170
  val b = randomInt(0, 9)
171
171
  val c = randomInt(0, 9)
172
172
  println(a.show ++ " " ++ b.show ++ " " ++ c.show)
173
- println(a*100 + b*10 + c)
173
+ println(a * 100 + b * 10 + c)
174
174
  }
175
175
  }
176
176
 
@@ -2,6 +2,7 @@ module stream
2
2
 
3
3
  import effekt
4
4
  import option
5
+ import exception
5
6
 
6
7
  // Effects
7
8
  // -------
@@ -128,6 +129,26 @@ def iterate[A](initial: A) { step: A => A }: Unit / emit[A] = {
128
129
  }
129
130
  }
130
131
 
132
+ /// Re-emit from the `stream` as long as the `predicate` holds.
133
+ /// Once the predicate fails, the stream ends.
134
+ ///
135
+ /// Examples:
136
+ /// ```effekt
137
+ /// > list::collect[Int] { takeWhile[Int] { x => x < 3 } { [1, 2, 3, 4, 1].each } }
138
+ /// [1, 2]
139
+ ///
140
+ /// > list::collect[Int] { takeWhile[Int] { x => x < 10 } { [1, 2, 3].each } }
141
+ /// [1, 2, 3]
142
+ ///
143
+ /// > list::collect[Int] { takeWhile[Int] { x => x < 0 } { [1, 2, 3].each } }
144
+ /// []
145
+ /// ```
146
+ def takeWhile[A] { predicate: A => Bool } { stream: () => Unit / emit[A] }: Unit / emit[A] =
147
+ try { stream() } with emit[A] {
148
+ case value and predicate(value) => do emit(value); resume(())
149
+ case _ => () // end the stream
150
+ }
151
+
131
152
 
132
153
  def sum { stream: () => Unit / emit[Int] }: Int =
133
154
  returning::sum[Unit]{stream}.second
@@ -135,6 +156,71 @@ def sum { stream: () => Unit / emit[Int] }: Int =
135
156
  def product { stream: () => Unit / emit[Int] }: Int =
136
157
  returning::product[Unit]{stream}.second
137
158
 
159
+ /// Returns true if all elements in the stream satisfy the given predicate,
160
+ /// false otherwise. Short-circuits as soon as a non-matching element is found.
161
+ ///
162
+ /// Examples:
163
+ /// ```effekt
164
+ /// > all[Int] { x => x > 0 } { [1, 2, 3].each }
165
+ /// true
166
+ ///
167
+ /// > all[Int] { x => x < 3 } { [1, 2, 3, 4].each }
168
+ /// false
169
+ ///
170
+ /// > all[Bool] { x => x } { [false, true, true].each }
171
+ /// false
172
+ ///
173
+ /// > all[Int] { x => x >= 0 } { () } // empty stream
174
+ /// true
175
+ /// ```
176
+ def all[A] { cond: A => Bool } { stream: => Unit / emit[A] }: Bool =
177
+ try { stream(); true } with emit[A] {
178
+ case value and not(cond(value)) => false
179
+ case _ => resume(())
180
+ }
181
+
182
+ /// Returns true if any element in the stream satisfies the given predicate,
183
+ /// false otherwise. Short-circuits as soon as a matching element is found.
184
+ ///
185
+ /// Examples:
186
+ /// ```effekt
187
+ /// > any[Int] { x => x > 0 } { [-2, -1, 0, 1, 2].each }
188
+ /// true
189
+ ///
190
+ /// > any[Int] { x => x > 10 } { [1, 2, 3, 4].each }
191
+ /// false
192
+ ///
193
+ /// > any[Bool] { x => x } { [false, true, false].each }
194
+ /// true
195
+ ///
196
+ /// > any[Int] { x => x == 0 } { () } // empty stream
197
+ /// false
198
+ /// ```
199
+ def any[A] { cond: A => Bool } { stream: => Unit / emit[A] }: Bool =
200
+ try { stream(); false } with emit[A] {
201
+ case value and cond(value) => true
202
+ case _ => resume(())
203
+ }
204
+
205
+ /// Count the number of emitted values that satisfy the given predicate.
206
+ ///
207
+ /// Examples:
208
+ /// ```
209
+ /// > countIf[Int] { x => x > 0 } { [2, -1, 0, 1, -2].each }
210
+ /// 2
211
+ ///
212
+ /// > countIf[Bool] { x => x } { [false, false, true, false].each }
213
+ /// 1
214
+ /// ```
215
+ def countIf[A] { shouldCount: A => Bool } { stream: () => Unit / emit[A] }: Int = {
216
+ var count = 0
217
+ for {stream} {
218
+ case x and shouldCount(x) => count = count + 1
219
+ case _ => ()
220
+ }
221
+ count
222
+ }
223
+
138
224
  def source[A] { stream: () => Unit / emit[A] } { reader: () => Unit / read[A] }: Unit =
139
225
  returning::source[A, Unit]{stream}{reader}
140
226
 
@@ -177,9 +263,9 @@ namespace internal {
177
263
 
178
264
  /// Use cons to handle prod, but also emit to the outside.
179
265
  /// The outside controls iteration, i.e., if cons aborts, the rest of prod will still be emitted.
180
- ///
266
+ ///
181
267
  /// Starts by executing cons. Will stop cons during execution if the outside stops consuming.
182
- ///
268
+ ///
183
269
  /// Example, printing all values consumed by the outside:
184
270
  ///
185
271
  /// with teeing{ {s} => for{s}{ e => println(e) } }
@@ -215,9 +301,9 @@ def teeing[A]{ cons: { => Unit / emit[A] } => Unit }{ prod: => Unit / emit[A] }:
215
301
  }
216
302
 
217
303
  /// Binds a `teeing`-like function in the body, stopping the inner producer once all consumers in tees are done consuming.
218
- ///
304
+ ///
219
305
  /// Example of use, equivalent to `tee[A]{cns1}{cns2}{prd}`:
220
- ///
306
+ ///
221
307
  /// manyTee[A] { {tee} =>
222
308
  /// with tee{cns1}
223
309
  /// with tee{cns2}
@@ -253,6 +339,25 @@ def tee[A]{ cons1: { => Unit / emit[A] } => Unit }{ cons2: { => Unit / emit[A] }
253
339
  }
254
340
  }
255
341
 
342
+ /// Return exactly the very first element of the given stream
343
+ /// or raise MissingValue if no elements are emitted.
344
+ ///
345
+ /// Examples:
346
+ /// ```effekt
347
+ /// > first[Int] { [1, 2, 3].each }
348
+ /// 1
349
+ /// > first[Bool] { do emit(true) }
350
+ /// true
351
+ /// > first[String] { () } // empty stream
352
+ /// <Exception MissingValue: code in first did not emit any values>
353
+ /// ```
354
+ def first[A] { body: => Unit / emit[A] }: A / Exception[MissingValue] =
355
+ try {
356
+ body()
357
+ val r: A = do raise[MissingValue](MissingValue(), "code in first did not emit any values")
358
+ r
359
+ } with emit[A] { a => a }
360
+
256
361
  namespace returning {
257
362
 
258
363
  /// Canonical handler of push streams that performs `action` for every
@@ -15,9 +15,9 @@ namespace internal {
15
15
  def parent(u: UnionFind, s: Int): Int / Exception[MissingValue] = {
16
16
  with on[OutOfBounds].default { do raise(MissingValue(), "No node " ++ s.show) }
17
17
  val c = u.rawElements.get(s)
18
- if (c < 0) {
19
- do raise(MissingValue(), "Root node " ++ s.show ++ " has no parent.")
20
- } else {
18
+ if (c < 0) {
19
+ do raise(MissingValue(), "Root node " ++ s.show ++ " has no parent.")
20
+ } else {
21
21
  c
22
22
  }
23
23
  }
@@ -31,11 +31,11 @@ namespace internal {
31
31
  }
32
32
  }
33
33
  def setParent(u: UnionFind, child: Int, parent: Int) = {
34
- with on[OutOfBounds].default { do raise(MissingValue(), "No node " ++ child.show) }
34
+ with default[OutOfBounds, Unit] { do raise(MissingValue(), "No node " ++ child.show) }
35
35
  u.rawElements.set(child, parent)
36
36
  }
37
37
  def setRank(u: UnionFind, root: Int, rank: Int) = {
38
- with on[OutOfBounds].default { do raise(MissingValue(), "No node " ++ root.show) }
38
+ with default[OutOfBounds, Unit] { do raise(MissingValue(), "No node " ++ root.show) }
39
39
  u.rawElements.set(root, -1 * rank - 1)
40
40
  }
41
41
  }
@@ -66,7 +66,7 @@ def find(u: UnionFind, s: Int): Int / Exception[MissingValue] = {
66
66
  }
67
67
  val root = goFind(s)
68
68
 
69
- def goCompress(s: Int): Unit = {
69
+ def goCompress(s: Int): Unit = {
70
70
  if (not(u.internal::isRoot(s))) {
71
71
  val p = u.internal::parent(s)
72
72
  u.internal::setParent(s, root)
@@ -82,7 +82,7 @@ def find(u: UnionFind, s: Int): Int / Exception[MissingValue] = {
82
82
  ///
83
83
  /// O(1)
84
84
  def unionRoots(u: UnionFind, x: Int, y: Int): Int / Exception[MissingValue] = {
85
- if(not(u.internal::isRoot(x) && u.internal::isRoot(y))) {
85
+ if(not(u.internal::isRoot(x) && u.internal::isRoot(y))) {
86
86
  panic("unionRoots should only be called on representatives")
87
87
  }
88
88
 
@@ -56,6 +56,6 @@ $effekt.println = function println$impl(str) {
56
56
 
57
57
  $effekt.unit = { __unit: true }
58
58
 
59
- $effekt.emptyMatch = function() { throw "empty match" }
59
+ $effekt.unreachable = function() { throw "should not be reachable!" }
60
60
 
61
61
  $effekt.hole = function(pos) { throw pos + " not implemented yet" }
@@ -7,8 +7,8 @@ declare void @c_io_println(%Pos)
7
7
  declare %Pos @c_io_readln()
8
8
  declare %Double @c_io_random()
9
9
 
10
- declare void @hole(i8*)
11
- declare void @duplicated_prompt()
10
+ declare void @hole(i8*) cold
11
+ declare void @duplicated_prompt() cold
12
12
 
13
13
  declare %Pos @c_ref_fresh(%Pos)
14
14
  declare %Pos @c_ref_get(%Pos)