@effekt-lang/effekt 0.46.0 → 0.48.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.
Files changed (41) hide show
  1. package/bin/effekt +0 -0
  2. package/libraries/chez/common/immutable/cslist.effekt +6 -6
  3. package/libraries/chez/common/mutable/dict.effekt +7 -7
  4. package/libraries/common/args.effekt +10 -10
  5. package/libraries/common/array.effekt +57 -4
  6. package/libraries/common/bench.effekt +2 -2
  7. package/libraries/common/buffer.effekt +5 -5
  8. package/libraries/common/bytearray.effekt +69 -7
  9. package/libraries/common/char.effekt +95 -8
  10. package/libraries/common/effekt.effekt +74 -71
  11. package/libraries/common/exception.effekt +1 -1
  12. package/libraries/common/io/console.effekt +4 -4
  13. package/libraries/common/io/error.effekt +12 -1
  14. package/libraries/common/io/filesystem.effekt +78 -16
  15. package/libraries/common/io/network.effekt +97 -65
  16. package/libraries/common/io/time.effekt +1 -1
  17. package/libraries/common/io.effekt +7 -8
  18. package/libraries/common/json.effekt +5 -5
  19. package/libraries/common/list.effekt +154 -18
  20. package/libraries/common/map.effekt +35 -1
  21. package/libraries/common/process.effekt +1 -1
  22. package/libraries/common/queue.effekt +3 -3
  23. package/libraries/common/random.effekt +1 -0
  24. package/libraries/common/ref.effekt +8 -4
  25. package/libraries/common/regex.effekt +4 -4
  26. package/libraries/common/seq.effekt +1 -1
  27. package/libraries/common/set.effekt +24 -1
  28. package/libraries/common/stream.effekt +2 -360
  29. package/libraries/common/string.effekt +35 -5
  30. package/libraries/common/stringbuffer.effekt +1 -0
  31. package/libraries/js/mutable/map.effekt +5 -5
  32. package/libraries/js/web/dom.effekt +11 -11
  33. package/libraries/llvm/io.c +281 -8
  34. package/libraries/llvm/main.c +1 -2
  35. package/libraries/llvm/rts.ll +14 -7
  36. package/libraries/llvm/types.c +3 -3
  37. package/licenses/eclipse public license, version 2.0 - epl-2.0.html +2 -2
  38. package/licenses/mit license - mit-license.html +48 -48
  39. package/licenses/the bsd license - bsd-license.html +50 -50
  40. package/licenses/the mit license - mit.html +48 -48
  41. package/package.json +1 -1
@@ -71,6 +71,77 @@ def appendFile(path: String, contents: String): Unit / Exception[IOError] = {
71
71
  go()
72
72
  }
73
73
 
74
+ def writeFileUTF8[R](path: String) { stream: () => R / emit[Char] }: R / Exception[IOError] =
75
+ writeFile(path) { encodeUTF8 { stream() } }
76
+
77
+ def readFileUTF8[R](path: String) { reader: () => R / read[Char] }: R / Exception[IOError] =
78
+ readFile(path) { decodeUTF8 { reader() } }
79
+
80
+ def writeFile[R](path: String) { stream: () => R / emit[Byte] }: R / Exception[IOError] = {
81
+
82
+ val file = openForWriting(path);
83
+ with on[IOError].finalize { close(file) }
84
+
85
+ val chunkSize = 1048576 // 1MB
86
+ val buffer = bytearray::allocate(chunkSize)
87
+ var offset = 0
88
+
89
+ def push(i: Int, n: Int): Unit = {
90
+ val r = write(file, buffer, i, n, -1)
91
+ if (r < n) {
92
+ push(i + r, n - r)
93
+ }
94
+ }
95
+
96
+ try {
97
+ val r = stream()
98
+ push(0, offset)
99
+ return r
100
+ } with emit[Byte] { (byte) =>
101
+ if (offset >= buffer.size) {
102
+ push(0, buffer.size)
103
+ offset = 0
104
+ }
105
+ buffer.unsafeSet(offset, byte)
106
+ offset = offset + 1
107
+ resume(())
108
+ }
109
+ }
110
+
111
+ def readFile[R](path: String) { reader: () => R / read[Byte] }: R / Exception[IOError] = {
112
+
113
+ val file = openForReading(path);
114
+ with on[IOError].finalize { close(file) }
115
+
116
+ val chunkSize = 1048576 // 1MB
117
+ val buffer = bytearray::allocate(chunkSize)
118
+ var offset = 0
119
+ var length = 0
120
+
121
+ def pull(): Unit / stop = {
122
+ read(file, buffer, 0, chunkSize, -1) match {
123
+ case 0 =>
124
+ do stop()
125
+ case n =>
126
+ length = n
127
+ }
128
+ }
129
+
130
+ try {
131
+ reader()
132
+ } with read[Byte] {
133
+ resume {
134
+ if (offset >= length) {
135
+ pull()
136
+ offset = 0
137
+ }
138
+ val byte = buffer.unsafeGet(offset)
139
+ offset = offset + 1
140
+ return byte
141
+ }
142
+ }
143
+ }
144
+
74
145
  /// An abstract interface applications can program against.
75
146
  ///
76
147
  /// Can be interpreted with the `filesystem` handler, or virtualized etc.
@@ -171,64 +242,55 @@ namespace internal {
171
242
  declare void @c_fs_mkdir(%Pos, %Stack)
172
243
  """
173
244
 
174
- extern async def openForReading(path: String): Int =
245
+ extern def openForReading(path: String) at async: Int =
175
246
  jsNode "$effekt.capture(callback => open(${path}, 'r', callback))"
176
247
  llvm """
177
248
  call void @c_fs_open_reading(%Pos ${path}, %Stack %stack)
178
249
  ret void
179
250
  """
180
251
 
181
- extern async def openForWriting(path: String): Int =
252
+ extern def openForWriting(path: String) at async: Int =
182
253
  jsNode "$effekt.capture(callback => open(${path}, 'w', callback))"
183
254
  llvm """
184
255
  call void @c_fs_open_writing(%Pos ${path}, %Stack %stack)
185
256
  ret void
186
257
  """
187
258
 
188
- extern async def openForAppending(path: String): Int =
259
+ extern def openForAppending(path: String) at async: Int =
189
260
  jsNode "$effekt.capture(callback => open(${path}, 'a', callback))"
190
261
  llvm """
191
262
  call void @c_fs_open_appending(%Pos ${path}, %Stack %stack)
192
263
  ret void
193
264
  """
194
265
 
195
- /// The buffer must be kept alive by using it after the call
196
- extern async def read(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int): Int =
266
+ extern def read(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int) at async: Int =
197
267
  jsNode "$effekt.capture(callback => read(${file}, ${buffer}, ${offset}, ${size}, ${position}, callback))"
198
268
  llvm """
199
269
  call void @c_fs_read(%Int ${file}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Int ${position}, %Stack %stack)
200
270
  ret void
201
271
  """
202
272
 
203
- /// The buffer must be kept alive by using it after the call
204
- extern async def write(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int): Int =
273
+ extern def write(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int) at async: Int =
205
274
  jsNode "$effekt.capture(callback => write(${file}, ${buffer}, ${offset}, ${size}, ${position}, callback))"
206
275
  llvm """
207
276
  call void @c_fs_write(%Int ${file}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Int ${position}, %Stack %stack)
208
277
  ret void
209
278
  """
210
279
 
211
- extern async def close(file: Int): Int =
280
+ extern def close(file: Int) at async: Int =
212
281
  jsNode "$effekt.capture(callback => close(${file}, callback))"
213
282
  llvm """
214
283
  call void @c_fs_close(%Int ${file}, %Stack %stack)
215
284
  ret void
216
285
  """
217
286
 
218
- extern async def mkdir(path: String): Int =
287
+ extern def mkdir(path: String) at async: Int =
219
288
  jsNode "$effekt.capture(callback => mkdir(${path}, callback))"
220
289
  llvm """
221
290
  call void @c_fs_mkdir(%Pos ${path}, %Stack %stack)
222
291
  ret void
223
292
  """
224
293
 
225
- def checkResult(result: Int): Int / Exception[IOError] =
226
- if (result < 0) {
227
- val ioError = fromNumber(internal::errorNumber(result));
228
- do raise[IOError](ioError, message(ioError))
229
- } else {
230
- result
231
- }
232
294
  }
233
295
 
234
296
  namespace examples {
@@ -1,84 +1,116 @@
1
1
  module io/network
2
2
 
3
+ import stream
3
4
  import bytearray
4
- import io
5
5
 
6
- namespace js {
7
- extern jsNode """
8
- const net = require('node:net');
6
+ import io/error
9
7
 
10
- function listen(server, port, host, listener) {
11
- server.listen(port, host);
12
- server.on('connection', listener);
13
- }
14
- """
15
8
 
16
- extern type JSServer // = net.Server
17
- extern type JSSocket // = net.Socket
18
- extern io def server(): JSServer =
19
- jsNode "net.createServer()"
20
- extern io def listen(server: JSServer, port: Int, host: String, listener: JSSocket => Unit at {io, async, global}): Unit =
21
- jsNode "listen(${server}, ${port}, ${host}, (socket) => $effekt.runToplevel((ks, k) => (${listener})(socket, ks, k)))"
9
+ /// A TCP handle. Should not be inspected.
10
+ type Connection = Int
22
11
 
23
- extern async def send(socket: JSSocket, data: ByteArray): Unit =
24
- jsNode "$effekt.capture(callback => ${socket}.write(${data}, callback))"
12
+ /// Reads data from a TCP connection into a buffer at the given offset.
13
+ def read(handle: Connection, buffer: ByteArray, offset: Int, size: Int): Int / Exception[IOError] =
14
+ internal::checkResult(internal::read(handle, buffer, offset, size))
25
15
 
26
- extern async def receive(socket: JSSocket): ByteArray =
27
- jsNode "$effekt.capture(callback => ${socket}.once('data', callback))"
28
-
29
- extern async def end(socket: JSSocket): Unit =
30
- jsNode "$effekt.capture(k => ${socket}.end(k))"
31
- }
16
+ /// Writes data from a buffer at the given offset to a TCP connection.
17
+ def write(handle: Connection, buffer: ByteArray, offset: Int, size: Int): Int / Exception[IOError] =
18
+ internal::checkResult(internal::write(handle, buffer, offset, size))
32
19
 
33
- interface Socket {
34
- def send(message: ByteArray): Unit
35
- def receive(): ByteArray
36
- def end(): Unit
37
- }
38
-
39
- def server(host: String, port: Int, handler: () => Unit / Socket at {io, async, global}): Unit = {
40
- val server = js::server();
41
- js::listen(server, port, host, box { socket =>
42
- println("New connection")
43
- spawn(box {
44
- try handler()
45
- with Socket {
46
- def send(message) =
47
- resume(js::send(socket, message))
48
- def receive() =
49
- resume(js::receive(socket))
50
- def end() =
51
- resume(js::end(socket))
52
- }
53
- })
54
- })
55
- }
20
+ /// Establishes a TCP connection to the specified host and port.
21
+ def connect(host: String, port: Int): Connection / Exception[IOError] =
22
+ internal::checkResult(internal::connect(host, port))
56
23
 
24
+ /// Closes a TCP connection and releases associated resources.
25
+ def close(handle: Connection): Unit =
26
+ internal::close(handle)
57
27
 
58
- namespace examples {
59
- def helloWorldApp(): Unit / Socket = {
60
- val request = do receive().toString;
28
+ /// A TCP listener. Should not be inspected.
29
+ type Listener = Int
61
30
 
62
- println("Received a request: " ++ request)
31
+ /// Creates a TCP listener bound to the specified host and port.
32
+ def bind(host: String, port: Int): Listener / Exception[IOError] =
33
+ internal::checkResult(internal::bind(host, port))
63
34
 
64
- def respond(s: String): Unit / Socket =
65
- do send(s.fromString)
35
+ /// Starts listening for incoming connections and handles them with the provided handler function.
36
+ /// Runs until `shutdown` is called on this `Listener`.
37
+ def listen(listener: Listener, handler: Connection => Unit at {io, async, global}): Unit / Exception[IOError] = {
38
+ internal::checkResult(internal::listen(listener, handler)); ()
39
+ }
66
40
 
67
- if (request.startsWith("GET /")) {
68
- respond("HTTP/1.1 200 OK\r\n\r\nHello from Effekt!")
69
- } else {
70
- respond("HTTP/1.1 400 Bad Request\r\n\r\n")
41
+ /// Stops a TCP listener and releases associated resources.
42
+ def shutdown(listener: Listener): Unit =
43
+ internal::shutdown(listener)
44
+
45
+
46
+ def streaming[R](connection: Connection) { program: () => R / {read[ByteArray], emit[ByteArray]} }: R / Exception[IOError] =
47
+ try {
48
+ program()
49
+ } with emit[ByteArray] { bytes =>
50
+ write(connection, bytes, 0, bytes.size)
51
+ resume(())
52
+ } with read[ByteArray] { () =>
53
+ val bytes = bytearray::allocate(4096)
54
+ read(connection, bytes, 0, bytes.size) match {
55
+ case r and r == bytes.size => resume { return bytes }
56
+ case r => resume { bytes.resize(r) }
71
57
  }
72
- do end()
73
58
  }
74
59
 
75
- // A server that just shows "Hello from Effekt!" on localhost:8080
76
- def main() = {
77
- val port = 8080
78
- println("Starting server on http://localhost:" ++ port.show)
60
+ namespace internal {
61
+
62
+ extern llvm """
63
+ declare void @c_tcp_connect(%Pos, %Int, %Stack)
64
+ declare void @c_tcp_read(%Int, %Pos, %Int, %Int, %Stack)
65
+ declare void @c_tcp_write(%Int, %Pos, %Int, %Int, %Stack)
66
+ declare void @c_tcp_close(%Int, %Stack)
67
+ declare void @c_tcp_bind(%Pos, %Int, %Int, %Stack)
68
+ declare void @c_tcp_listen(%Int, %Pos, %Stack)
69
+ declare void @c_tcp_shutdown(%Int, %Stack)
70
+ """
71
+
72
+ extern def connect(host: String, port: Int) at async: Int =
73
+ llvm """
74
+ call void @c_tcp_connect(%Pos ${host}, %Int ${port}, %Stack %stack)
75
+ ret void
76
+ """
77
+
78
+ extern def read(handle: Int, buffer: ByteArray, offset: Int, size: Int) at async: Int =
79
+ llvm """
80
+ call void @c_tcp_read(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
81
+ ret void
82
+ """
83
+
84
+ extern def write(handle: Int, buffer: ByteArray, offset: Int, size: Int) at async: Int =
85
+ llvm """
86
+ call void @c_tcp_write(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
87
+ ret void
88
+ """
89
+
90
+ extern def close(handle: Int) at async: Unit =
91
+ llvm """
92
+ call void @c_tcp_close(%Int ${handle}, %Stack %stack)
93
+ ret void
94
+ """
95
+
96
+ extern def bind(host: String, port: Int) at io: Int =
97
+ llvm """
98
+ %result = call %Int @c_tcp_bind(%Pos ${host}, %Int ${port})
99
+ ret %Int %result
100
+ """
101
+
102
+ extern def listen(listener: Int, handler: Int => Unit at {io, async, global}) at async: Int =
103
+ llvm """
104
+ call void @c_tcp_listen(%Int ${listener}, %Pos ${handler}, %Stack %stack)
105
+ ret void
106
+ """
107
+
108
+ extern def shutdown(handle: Int) at async: Unit =
109
+ llvm """
110
+ call void @c_tcp_shutdown(%Int ${handle}, %Stack %stack)
111
+ ret void
112
+ """
79
113
 
80
- server("localhost", port, box {
81
- helloWorldApp()
82
- })
83
- }
84
114
  }
115
+
116
+
@@ -4,7 +4,7 @@ extern llvm """
4
4
  declare void @c_timer_start(%Int, %Stack)
5
5
  """
6
6
 
7
- extern async def wait(millis: Int): Unit =
7
+ extern def wait(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)
@@ -12,23 +12,22 @@ extern llvm """
12
12
  declare void @c_yield(%Stack)
13
13
  """
14
14
 
15
- extern async def spawn(task: Task[Unit]): Unit =
15
+ extern def spawn(task: Task[Unit]) at async: Unit =
16
16
  js "$effekt.capture(k => { setTimeout(() => k($effekt.unit), 0); return $effekt.run(${task}) })"
17
17
  llvm """
18
18
  call void @c_yield(%Stack %stack)
19
- %unboxed = call ccc %Neg @coercePosNeg(%Pos ${task})
20
- call void @run(%Neg %unboxed)
19
+ call void @run(%Pos ${task})
21
20
  ret void
22
21
  """
23
22
 
24
- extern async def yield(): Unit =
23
+ extern def yield() at async: Unit =
25
24
  js "$effekt.capture(k => setTimeout(() => k($effekt.unit), 0))"
26
25
  llvm """
27
26
  call void @c_yield(%Stack %stack)
28
27
  ret void
29
28
  """
30
29
 
31
- extern async def abort(): Nothing =
30
+ extern def abort() at async: Nothing =
32
31
  js "$effekt.capture(k => undefined)"
33
32
  llvm """
34
33
  call void @eraseStack(%Stack %stack)
@@ -55,14 +54,14 @@ extern llvm """
55
54
  declare void @c_promise_await(%Pos, %Neg)
56
55
  """
57
56
 
58
- extern async def await[T](promise: Promise[T]): T =
57
+ extern def await[T](promise: Promise[T]) at async: T =
59
58
  js "$effekt.capture(k => ${promise}.promise.then(k))"
60
59
  llvm """
61
60
  call void @c_promise_await(%Pos ${promise}, %Stack %stack)
62
61
  ret void
63
62
  """
64
63
 
65
- extern async def resolve[T](promise: Promise[T], value: T): Unit =
64
+ extern def resolve[T](promise: Promise[T], value: T) at async: Unit =
66
65
  js "$effekt.capture(k => { ${promise}.resolve(${value}); return k($effekt.unit) })"
67
66
  llvm """
68
67
  call void @c_promise_resolve(%Pos ${promise}, %Pos ${value}, %Stack %stack)
@@ -80,7 +79,7 @@ namespace promise {
80
79
  }
81
80
  """
82
81
 
83
- extern io def make[T](): Promise[T] =
82
+ extern def make[T]() at io: Promise[T] =
84
83
  js "promise$make()"
85
84
  llvm """
86
85
  %promise = call %Pos @c_promise_make()
@@ -165,7 +165,7 @@ def decodeJson(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
165
165
  case 'f' => expectString("false"); do bool(false)
166
166
  case '-' => do number(readDouble())
167
167
  case d and d.isDigit => do number(readDouble())
168
- case '"' => do string(collectString { readQuotedString() })
168
+ case '"' => do string(collect { readQuotedString() })
169
169
  case '{' => do dict{ decodeJsonObject() }
170
170
  case '[' => do list{ decodeJsonList() }
171
171
  case _ => println("Unexpected " ++ c.toString); <>
@@ -179,7 +179,7 @@ def decodeJsonObject(): Unit / {Scan[Char], JsonObjectBuilder, Exception[WrongFo
179
179
  skipWhitespace()
180
180
  while(do peek[Char]() is c and (c != '}')) {
181
181
  if (not(first)) { expectString(",") }
182
- val k: String = collectString { readQuotedString() }
182
+ val k = string::collect { readQuotedString() }
183
183
  skipWhitespace()
184
184
  expectString(":")
185
185
  do field(k){
@@ -277,7 +277,7 @@ def build[R](){ body: => R / JsonBuilder }: (R, JsonValue) = {
277
277
  }
278
278
  (x, r)
279
279
  }
280
- def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = returning::collectList[JsonValue, R] {
280
+ def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = list::returning::collect[JsonValue, R] {
281
281
  try body() with JsonBuilder {
282
282
  def number(n) = { do emit(Number(n)); resume(()) }
283
283
  def bool(b) = { do emit(Bool(b)); resume(()) }
@@ -295,7 +295,7 @@ def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = returning
295
295
  }
296
296
  }
297
297
  }
298
- def buildDict[R](){ body: => R / JsonObjectBuilder }: (R, List[(String, JsonValue)]) = returning::collectList[(String, JsonValue), R] {
298
+ def buildDict[R](){ body: => R / JsonObjectBuilder }: (R, List[(String, JsonValue)]) = list::returning::collect[(String, JsonValue), R] {
299
299
  try body() with JsonObjectBuilder {
300
300
  def field(k) = resume { {v} =>
301
301
  val x = build{v}
@@ -315,7 +315,7 @@ namespace test {
315
315
  // Read quoted string
316
316
  feed("\"\ta\n\ra\"") {
317
317
  with scanner[Char]
318
- println(collectString { readQuotedString() })
318
+ println(string::collect { readQuotedString() })
319
319
  }
320
320
 
321
321
  println("")
@@ -3,6 +3,7 @@ module list
3
3
  import effekt
4
4
  import option
5
5
  import exception
6
+ import stream
6
7
 
7
8
  /// Immutable linked list for finite sequences of elements.
8
9
  type List[A] {
@@ -167,9 +168,52 @@ def foreachIndex[A](list: List[A]){ f: (Int, A) {Control} => Unit }: Unit = {
167
168
  /// Map a function `f` over elements in a given list.
168
169
  ///
169
170
  /// O(N)
170
- def map[A, B](l: List[A]) { f: A => B } : List[B] = {
171
- var acc = Nil[B]()
172
- l.foreach { el => acc = Cons(f(el), acc) }
171
+ def map[A, B](l: List[A]) { f: A => B }: List[B] = {
172
+ var acc: List[B] = Nil()
173
+ l.foreach { el =>
174
+ acc = Cons(f(el), acc)
175
+ }
176
+ acc.reverse
177
+ }
178
+
179
+ /// Map a function `f` over elements and their indices in a given list.
180
+ ///
181
+ /// O(N)
182
+ def mapWithIndex[A, B](l: List[A]) { f: (Int, A) => B }: List[B] = {
183
+ var acc: List[B] = Nil()
184
+ l.foreachIndex { (i, el) =>
185
+ acc = Cons(f(i, el), acc)
186
+ }
187
+ acc.reverse
188
+ }
189
+
190
+ /// Map a function `f` over elements in a given list,
191
+ /// keeping only the elements for which the function returned `true`,
192
+ /// discarding the elements for which the function returned `false`.
193
+ ///
194
+ /// O(N)
195
+ def filter[A](l: List[A]) { shouldKeep: A => Bool }: List[A] = {
196
+ var acc: List[A] = Nil()
197
+ l.foreach { a =>
198
+ if (shouldKeep(a)) {
199
+ acc = Cons(a, acc)
200
+ }
201
+ }
202
+ acc.reverse
203
+ }
204
+
205
+ /// Map a function `f` over elements and their indices in a given list,
206
+ /// keeping only the elements for which the function returned `true`,
207
+ /// discarding the elements for which the function returned `false`.
208
+ ///
209
+ /// O(N)
210
+ def filterWithIndex[A](l: List[A]) { shouldKeep: (Int, A) => Bool }: List[A] = {
211
+ var acc: List[A] = Nil()
212
+ l.foreachIndex { (i, a) =>
213
+ if (shouldKeep(i, a)) {
214
+ acc = Cons(a, acc)
215
+ }
216
+ }
173
217
  acc.reverse
174
218
  }
175
219
 
@@ -178,13 +222,28 @@ def map[A, B](l: List[A]) { f: A => B } : List[B] = {
178
222
  /// discarding the elements for which the function returned `None()`.
179
223
  ///
180
224
  /// O(N)
181
- def collect[A, B](l: List[A]) { f : A => Option[B] }: List[B] = {
182
- var acc = Nil[B]()
225
+ def collectSome[A, B](l: List[A]) { f : A => Option[B] }: List[B] = {
226
+ var acc: List[B] = Nil()
183
227
  l.foreach { a =>
184
- val optB = f(a)
185
- optB match {
186
- case None() => ();
187
- case Some(b) => acc = Cons(b, acc);
228
+ f(a) match {
229
+ case None() => ()
230
+ case Some(b) => acc = Cons(b, acc)
231
+ }
232
+ }
233
+ acc.reverse
234
+ }
235
+
236
+ /// Map a function `f` over elements and their indices in a given list,
237
+ /// keeping only the elements for which the function returned `Some(...)`,
238
+ /// discarding the elements for which the function returned `None()`.
239
+ ///
240
+ /// O(N)
241
+ def collectWithIndex[A, B](l: List[A]) { f: (Int, A) => Option[B] }: List[B] = {
242
+ var acc: List[B] = Nil()
243
+ l.foreachIndex { (i, a) =>
244
+ f(i, a) match {
245
+ case None() => ()
246
+ case Some(b) => acc = Cons(b, acc)
188
247
  }
189
248
  }
190
249
  acc.reverse
@@ -205,21 +264,55 @@ def flatMap[A, B](l: List[A]) { f : A => List[B] }: List[B] = {
205
264
  /// Check if predicate is true for all elements of the given list.
206
265
  ///
207
266
  /// O(N)
208
- def all[A](list: List[A]) { predicate: A => Bool }: Bool = {
267
+ def all[A](list: List[A]) { predicate: A => Bool }: Bool =
209
268
  list match {
210
269
  case Cons(x, xs) => predicate(x) && all(xs) { predicate }
211
270
  case Nil() => true
212
271
  }
213
- }
214
272
 
215
273
  /// Check if predicate is true for at least one element of the given list.
216
274
  ///
217
275
  /// O(N)
218
- def any[A](list: List[A]) { predicate: A => Bool }: Bool = {
276
+ def any[A](list: List[A]) { predicate: A => Bool }: Bool =
219
277
  list match {
220
278
  case Cons(x, xs) => predicate(x) || any(xs) { predicate }
221
279
  case Nil() => false
222
280
  }
281
+
282
+ /// Check if the list contains the given `element`,
283
+ /// comparing for equality with the given `eq` function.
284
+ ///
285
+ /// O(N)
286
+ def contains[A](list: List[A], element: A) { eq: (A, A) => Bool }: Bool =
287
+ list.any { x => x.eq(element) }
288
+
289
+ /// Find the first index of the given `element`,
290
+ /// comparing for equality with the given `eq` function.
291
+ ///
292
+ /// O(N)
293
+ def indexOf[A](list: List[A], element: A) { eq: (A, A) => Bool }: Option[Int] = {
294
+ var result = None()
295
+ list.foreachIndex { (i, a) {label} =>
296
+ if (element.eq(a)) {
297
+ result = Some(i)
298
+ label.break()
299
+ }
300
+ }
301
+ result
302
+ }
303
+
304
+ /// Find the last index of the given `element`,
305
+ /// comparing for equality with the given `eq` function.
306
+ ///
307
+ /// O(N)
308
+ def lastIndexOf[A](list: List[A], element: A) { eq: (A, A) => Bool }: Option[Int] = {
309
+ var result = None()
310
+ list.foreachIndex { (i, a) {label} =>
311
+ if (element.eq(a)) {
312
+ result = Some(i)
313
+ }
314
+ }
315
+ result
223
316
  }
224
317
 
225
318
  /// Count how many elements of a list satisfy the given predicate.
@@ -297,10 +390,11 @@ def reverse[A](l: List[A]): List[A] = {
297
390
  /// ```
298
391
  ///
299
392
  /// O(|l|)
300
- def reverseOnto[A](l: List[A], other: List[A]): List[A] = l match {
301
- case Nil() => other
302
- case Cons(a, rest) => rest.reverseOnto(Cons(a, other))
303
- }
393
+ def reverseOnto[A](l: List[A], other: List[A]): List[A] =
394
+ l match {
395
+ case Nil() => other
396
+ case Cons(a, rest) => rest.reverseOnto(Cons(a, other))
397
+ }
304
398
 
305
399
  /// Concatenate list `l` with list `other`:
306
400
  ///
@@ -347,7 +441,7 @@ def join[A](lists: List[List[A]]): List[A] = {
347
441
  /// ```
348
442
  ///
349
443
  /// O(N)
350
- def join[A](lists: List[List[A]], between: List[A]): List[A] = {
444
+ def join[A](lists: List[List[A]], between: List[A]): List[A] =
351
445
  lists match {
352
446
  case Nil() => Nil()
353
447
  case Cons(firstList, restOfLists) =>
@@ -355,7 +449,6 @@ def join[A](lists: List[List[A]], between: List[A]): List[A] = {
355
449
  restOfLists.flatMap { list => between.append(list)}
356
450
  )
357
451
  }
358
- }
359
452
 
360
453
  /// Take the first `n` elements of a given list.
361
454
  ///
@@ -836,3 +929,46 @@ def println(l: List[Int]): Unit = println(show(l))
836
929
  def println(l: List[Double]): Unit = println(show(l))
837
930
  def println(l: List[Bool]): Unit = println(show(l))
838
931
  def println(l: List[String]): Unit = println(show(l))
932
+
933
+
934
+ // Streaming
935
+ // ---------
936
+
937
+ /// Turns a `list` into a producer of a push stream
938
+ /// by emitting each contained value left-to-right.
939
+ def each[A](list: List[A]): Unit / emit[A] =
940
+ list match {
941
+ case Nil() => ()
942
+ case Cons(head, tail) =>
943
+ do emit(head)
944
+ each(tail)
945
+ }
946
+
947
+ def feed[T, R](list: List[T]) { reader: () => R / read[T] }: R = {
948
+ var l = list
949
+ try {
950
+ reader()
951
+ } with read[T] {
952
+ resume {
953
+ l match {
954
+ case Nil() => do stop()
955
+ case Cons(value, rest) =>
956
+ l = rest
957
+ return value
958
+ }
959
+ }
960
+ }
961
+ }
962
+
963
+ def collect[A] { stream: () => Unit / emit[A] }: List[A] =
964
+ returning::collect[A, Unit]{stream}.second
965
+
966
+ namespace returning {
967
+ def collect[A, R] { stream: () => R / emit[A] }: (R, List[A]) =
968
+ try {
969
+ (stream(), Nil())
970
+ } with emit[A] { (v) =>
971
+ val (r, vs) = resume(());
972
+ (r, Cons(v, vs))
973
+ }
974
+ }