@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.
@@ -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
  // -------
@@ -44,7 +45,7 @@ def fix[T] { one: T => Unit / emit[T] } { stream: => Unit / emit[T] }: Unit =
44
45
  try {
45
46
  stream()
46
47
  } with emit[T] { value =>
47
- fix[T] {one} { one(value) }
48
+ fix {one} { one(value) }
48
49
  resume(())
49
50
  }
50
51
 
@@ -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
@@ -5,7 +5,7 @@ import array
5
5
  // A mutable map, backed by a JavaScript Map.
6
6
  extern type Map[K, V]
7
7
 
8
- extern def emptyMap[K, V]() at {}: Map[K, V] =
8
+ extern def emptyMap[K, V]() at io: Map[K, V] =
9
9
  js "new Map()"
10
10
 
11
11
  def get[K, V](m: Map[K, V], key: K): Option[V] =
@@ -28,4 +28,4 @@ extern def values[K, V](map: Map[K, V]) at {}: Array[V] =
28
28
  js "Array.from(${map}.values())"
29
29
 
30
30
  extern def keys[K, V](map: Map[K, V]) at {}: Array[K] =
31
- js "Array.from(${map}.keys())"
31
+ js "Array.from(${map}.keys())"
@@ -572,154 +572,254 @@ void c_yield(Stack stack) {
572
572
  c_timer_start(0, stack);
573
573
  }
574
574
 
575
-
576
- // Promises
575
+ // Signals
577
576
  // --------
578
577
 
579
- typedef enum { UNRESOLVED, RESOLVED } promise_state_t;
580
-
581
- typedef struct Listeners {
582
- Stack head;
583
- struct Listeners* tail;
584
- } Listeners;
578
+ typedef enum { INITIAL, NOTIFIED } signal_state_t;
585
579
 
586
580
  typedef struct {
587
581
  uint64_t rc;
588
582
  void* eraser;
589
- promise_state_t state;
590
- // state of {
591
- // case UNRESOLVED => Possibly empty (head is NULL) list of listeners
592
- // case RESOLVED => Pos (the result)
593
- // }
594
- union {
595
- struct Pos value;
596
- Listeners listeners;
597
- } payload;
598
- } Promise;
599
-
600
- void c_promise_erase_listeners(void *envPtr) {
601
- // envPtr points to a Promise _after_ the eraser, so let's adjust it to point to the promise.
602
- Promise *promise = (Promise*) (envPtr - offsetof(Promise, state));
603
- promise_state_t state = promise->state;
604
-
605
- Stack head;
606
- Listeners* tail;
607
- Listeners* current;
608
-
609
- switch (state) {
610
- case UNRESOLVED:
611
- head = promise->payload.listeners.head;
612
- tail = promise->payload.listeners.tail;
613
- if (head != NULL) {
614
- // Erase head
615
- eraseStack(head);
616
- // Erase tail
617
- current = tail;
618
- while (current != NULL) {
619
- head = current->head;
620
- tail = current->tail;
621
- free(current);
622
- eraseStack(head);
623
- current = tail;
624
- };
625
- };
626
- break;
627
- case RESOLVED:
628
- erasePositive(promise->payload.value);
629
- break;
630
- }
631
- }
583
+ signal_state_t state;
584
+ struct Pos value;
585
+ Stack stack;
586
+ } Signal;
632
587
 
633
- void c_promise_resume_listeners(Listeners* listeners, struct Pos value) {
634
- if (listeners != NULL) {
635
- Stack head = listeners->head;
636
- Listeners* tail = listeners->tail;
637
- free(listeners);
638
- c_promise_resume_listeners(tail, value);
639
- sharePositive(value);
640
- resume_Pos(head, value);
641
- }
588
+ void c_signal_erase(void *envPtr) {
589
+ // envPtr points to a Signal _after_ the eraser, so let's adjust it to point to the beginning.
590
+ Signal *signal = (Signal*) (envPtr - offsetof(Signal, state));
591
+ erasePositive(signal->value);
592
+ if (signal->stack != NULL) { eraseStack(signal->stack); }
642
593
  }
643
594
 
644
- void c_promise_resolve(struct Pos promise, struct Pos value, Stack stack) {
645
- Promise* p = (Promise*)promise.obj;
595
+ struct Pos c_signal_make() {
596
+ Signal* f = (Signal*)malloc(sizeof(Signal));
646
597
 
647
- Stack head;
648
- Listeners* tail;
598
+ f->rc = 0;
599
+ f->eraser = c_signal_erase;
600
+ f->state = INITIAL;
601
+ f->value = (struct Pos) { .tag = 0, .obj = NULL, };
602
+ f->stack = NULL;
649
603
 
650
- switch (p->state) {
651
- case UNRESOLVED:
652
- head = p->payload.listeners.head;
653
- tail = p->payload.listeners.tail;
654
-
655
- p->state = RESOLVED;
656
- p->payload.value = value;
657
- resume_Pos(stack, Unit);
604
+ return (struct Pos) { .tag = 0, .obj = f, };
605
+ }
658
606
 
659
- if (head != NULL) {
660
- // Execute tail
661
- c_promise_resume_listeners(tail, value);
662
- // Execute head
663
- sharePositive(value);
664
- resume_Pos(head, value);
665
- };
607
+ void c_signal_notify(struct Pos signal, struct Pos value, Stack stack) {
608
+ Signal* f = (Signal*)signal.obj;
609
+ switch (f->state) {
610
+ case INITIAL: {
611
+ f->state = NOTIFIED;
612
+ f->value = value;
613
+ f->stack = stack;
614
+ erasePositive(signal);
666
615
  break;
667
- case RESOLVED:
668
- erasePositive(promise);
669
- erasePositive(value);
670
- eraseStack(stack);
671
- fprintf(stderr, "ERROR: Promise already resolved\n");
672
- exit(1);
616
+ }
617
+ case NOTIFIED: {
618
+ struct Pos other_value = f->value;
619
+ f->value = (struct Pos) { .tag = 0, .obj = NULL, };
620
+ Stack other_stack = f->stack;
621
+ f->stack = NULL;
622
+ erasePositive(signal);
623
+ resume_Pos(other_stack, value);
624
+ resume_Pos(stack, other_value);
673
625
  break;
626
+ }
674
627
  }
675
- // TODO stack overflow?
676
- // We need to erase the promise now, since we consume it.
677
- erasePositive(promise);
678
628
  }
679
629
 
680
- void c_promise_await(struct Pos promise, Stack stack) {
681
- Promise* p = (Promise*)promise.obj;
630
+ // Subprocesses
631
+ // ------------
682
632
 
683
- Stack head;
684
- Listeners* tail;
685
- Listeners* node;
686
- struct Pos value;
633
+ struct Pos c_spawn_options_default() {
634
+ uv_process_options_t* options = (uv_process_options_t*)malloc(sizeof(uv_process_options_t));
635
+ memset(options, 0, sizeof(uv_process_options_t));
636
+ uv_stdio_container_t* stdio = (uv_stdio_container_t*)malloc(3*sizeof(uv_stdio_container_t));
637
+ stdio[0].flags = UV_INHERIT_FD;
638
+ stdio[0].data.fd = 0;
639
+ stdio[1].flags = UV_INHERIT_FD;
640
+ stdio[1].data.fd = 1;
641
+ stdio[2].flags = UV_INHERIT_FD;
642
+ stdio[2].data.fd = 2;
643
+ options->stdio = stdio;
644
+ options->stdio_count = 3;
645
+ return (struct Pos) { .tag = 0, .obj = options, };
646
+ }
687
647
 
688
- switch (p->state) {
689
- case UNRESOLVED:
690
- head = p->payload.listeners.head;
691
- tail = p->payload.listeners.tail;
692
- if (head != NULL) {
693
- node = (Listeners*)malloc(sizeof(Listeners));
694
- node->head = head;
695
- node->tail = tail;
696
- p->payload.listeners.head = stack;
697
- p->payload.listeners.tail = node;
698
- } else {
699
- p->payload.listeners.head = stack;
700
- };
701
- break;
702
- case RESOLVED:
703
- value = p->payload.value;
704
- sharePositive(value);
705
- resume_Pos(stack, value);
706
- break;
707
- };
708
- // TODO hmm, stack overflow?
709
- erasePositive(promise);
648
+ static void subproc_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
649
+ (void)handle;
650
+ buf->base = malloc(suggested_size);
651
+ buf->len = suggested_size;
652
+ }
653
+
654
+ typedef struct {
655
+ struct Pos handler;
656
+ } subproc_stream_cb_closure_t;
657
+ void subproc_stream_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
658
+ subproc_stream_cb_closure_t* clos = (subproc_stream_cb_closure_t*)(stream->data);
659
+ if(nread >= 0) {
660
+ struct Pos chunk = c_bytearray_construct(nread, (uint8_t*)(buf->base));
661
+ if (buf->base) free(buf->base);
662
+ run_Pos(clos->handler, chunk);
663
+ } else {
664
+ uv_read_stop(stream);
665
+ erasePositive(clos->handler);
666
+ free(clos);
667
+ if (buf->base) free(buf->base);
668
+ }
669
+ }
670
+ struct Pos subproc_options_on_stream(struct Pos opts, size_t id, uv_stdio_flags flags, struct Pos callback) {
671
+ uv_process_options_t* options = (uv_process_options_t*)opts.obj;
672
+
673
+ options->stdio[id].flags = UV_CREATE_PIPE | flags;
674
+ uv_pipe_t* pipe = (uv_pipe_t*)malloc(sizeof(uv_pipe_t));
675
+
676
+ uv_pipe_init(uv_default_loop(), pipe, 1);
677
+ options->stdio[id].data.stream = (uv_stream_t*)pipe;
678
+
679
+ subproc_stream_cb_closure_t* clos = (subproc_stream_cb_closure_t*)malloc(sizeof(subproc_stream_cb_closure_t));
680
+ clos->handler = callback;
681
+ pipe->data = clos;
682
+
683
+ return opts;
684
+ }
685
+ struct Pos c_spawn_options_on_stdout(struct Pos opts, struct Pos callback) {
686
+ return subproc_options_on_stream(opts, 1, UV_WRITABLE_PIPE, callback);
687
+ }
688
+ struct Pos c_spawn_options_on_stderr(struct Pos opts, struct Pos callback) {
689
+ return subproc_options_on_stream(opts, 2, UV_WRITABLE_PIPE, callback);
690
+ }
691
+ struct Pos c_spawn_options_pipe_stdin(struct Pos opts, struct Pos callback) {
692
+ return subproc_options_on_stream(opts, 0, UV_READABLE_PIPE, callback);
710
693
  }
711
694
 
712
- struct Pos c_promise_make() {
713
- Promise* promise = (Promise*)malloc(sizeof(Promise));
714
695
 
715
- promise->rc = 0;
716
- promise->eraser = c_promise_erase_listeners;
717
- promise->state = UNRESOLVED;
718
- promise->payload.listeners.head = NULL;
719
- promise->payload.listeners.tail = NULL;
696
+ typedef struct {
697
+ char** args;
698
+ Stack k;
699
+ uv_process_options_t* opts;
700
+ } subproc_proc_data_t;
701
+ void c_close_stream_callback_exit(uv_handle_t* req) {
702
+ free(req);
703
+ }
704
+ void subproc_on_close(uv_handle_t* handle) {
705
+ subproc_proc_data_t* pd = (subproc_proc_data_t*)((uv_process_t*)handle)->data;
706
+ uv_process_options_t* opts = (uv_process_options_t*)(pd->opts);
707
+ if(opts->stdio[0].flags & UV_CREATE_PIPE){
708
+ uv_close((uv_handle_t*)(opts->stdio[0].data.stream), c_close_stream_callback_exit);
709
+ }
710
+ if(opts->stdio[1].flags & UV_CREATE_PIPE){
711
+ uv_close((uv_handle_t*)(opts->stdio[1].data.stream), c_close_stream_callback_exit);
712
+ }
713
+ if(opts->stdio[2].flags & UV_CREATE_PIPE){
714
+ uv_close((uv_handle_t*)(opts->stdio[2].data.stream), c_close_stream_callback_exit);
715
+ }
716
+ // TODO shutdown pipes for stdio
717
+ free(opts->stdio);
718
+ free(opts);
719
+ free(pd);
720
+ free(handle);
721
+ }
722
+ void subproc_on_exit(uv_process_t* proc, int64_t exit_status, int term_signal) {
723
+ (void)term_signal; (void)exit_status;
724
+ subproc_proc_data_t* pd = (subproc_proc_data_t*)proc->data;
725
+ if (pd->args) {
726
+ for(int i = 0; pd->args[i] != NULL; i++) {
727
+ free(pd->args[i]);
728
+ }
729
+ free(pd->args);
730
+ }
731
+ Stack k = pd->k;
732
+ uv_close((uv_handle_t*)proc, subproc_on_close);
733
+ resume_Int(k, exit_status);
734
+ }
720
735
 
721
- return (struct Pos) { .tag = 0, .obj = promise, };
736
+ void c_close_stream_callback(uv_shutdown_t* req, int status) {
737
+ if(status != 0){
738
+ // TODO error handling
739
+ }
740
+ free(req);
741
+ }
742
+ void c_close_stream(struct Pos stream) {
743
+ uv_stream_t* str = (uv_stream_t*)stream.obj;
744
+ uv_shutdown_t* req = (uv_shutdown_t*)malloc(sizeof(uv_shutdown_t));
745
+ uv_shutdown(req, str, c_close_stream_callback);
722
746
  }
723
747
 
748
+ typedef struct {
749
+ struct Pos buffer;
750
+ uv_buf_t buf;
751
+ } subproc_write_req_data_t;
752
+ void c_write_stream_callback(uv_write_t* req, int status) {
753
+ if(status != 0){
754
+ // TODO error handling
755
+ }
756
+ subproc_write_req_data_t* data = (subproc_write_req_data_t*)(req->data);
757
+ erasePositive(data->buffer);
758
+ free(data);
759
+ free(req);
760
+ }
761
+ void c_write_stream(struct Pos stream, struct Pos buffer) {
762
+ uv_stream_t* str = (uv_stream_t*)stream.obj;
763
+ uv_write_t* req = (uv_write_t*)malloc(sizeof(uv_write_t));
764
+ memset(req, 0, sizeof(uv_write_t));
765
+ subproc_write_req_data_t* data = (subproc_write_req_data_t*)malloc(sizeof(subproc_write_req_data_t));
766
+ data->buffer = buffer;
767
+ uv_buf_t buf = uv_buf_init((char*)(c_bytearray_data(buffer)), buffer.tag);
768
+ req->data = data;
769
+ data->buf = buf;
770
+ uv_write(req, str, &(data->buf), 1, c_write_stream_callback);
771
+ }
772
+
773
+ void c_spawn(struct Pos cmd, struct Pos args, struct Pos options, Stack stack) {
774
+ uv_process_options_t* opts = (uv_process_options_t*)options.obj;
775
+ uv_process_t* proc = (uv_process_t*)malloc(sizeof(uv_process_t));
776
+
777
+ // command
778
+ char* cmd_s = c_bytearray_into_nullterminated_string(cmd);
779
+ erasePositive(cmd);
780
+ opts->file = cmd_s;
781
+
782
+ // args
783
+ int _argc = (int)((uint64_t*)args.obj)[2];
784
+ char** _args = (char**)malloc((2 + _argc) * sizeof(char*));
785
+ struct Pos* _argv = (struct Pos*)(((uint64_t*)args.obj) + 3);
786
+ _args[0] = cmd_s;
787
+ for(int i = 0; i < _argc; i++) {
788
+ _args[i + 1] = c_bytearray_into_nullterminated_string(_argv[i]);
789
+ }
790
+ erasePositive(args);
791
+ _args[_argc + 1] = NULL;
792
+ opts->args = _args;
793
+ // callback to free _args
794
+ opts->exit_cb = subproc_on_exit;
795
+ subproc_proc_data_t* pd = (subproc_proc_data_t*)malloc(sizeof(subproc_proc_data_t));
796
+ pd->args = _args;
797
+ pd->k = stack;
798
+ proc->data = pd;
799
+
800
+ // spawn process
801
+ int err = uv_spawn(uv_default_loop(), proc, opts);
802
+
803
+ if(opts->stdio[0].flags & UV_CREATE_PIPE) {
804
+ uv_stream_t* stream = (uv_stream_t*)(opts->stdio[0].data.stream);
805
+ subproc_stream_cb_closure_t* clos = (subproc_stream_cb_closure_t*)(stream->data);
806
+ struct Pos str = (struct Pos) { .tag = 0, .obj = stream, };
807
+ run_Pos(clos->handler, str);
808
+ erasePositive(clos->handler);
809
+ free(clos);
810
+ }
811
+ if(opts->stdio[1].flags & UV_CREATE_PIPE)
812
+ uv_read_start((uv_stream_t*)(opts->stdio[1].data.stream), subproc_alloc_cb, subproc_stream_cb);
813
+ if(opts->stdio[2].flags & UV_CREATE_PIPE)
814
+ uv_read_start((uv_stream_t*)(opts->stdio[2].data.stream), subproc_alloc_cb, subproc_stream_cb);
815
+ pd->opts = opts;
816
+
817
+ erasePositive(options);
818
+ if(err) {
819
+ printf("%s\n", uv_strerror(err));
820
+ free(_args);
821
+ free(proc);
822
+ }
823
+ }
724
824
 
725
825
  #endif