@effekt-lang/effekt 0.23.0 → 0.25.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
@@ -3,7 +3,6 @@ module buffer
3
3
  import array
4
4
 
5
5
  // TODO (in Effekt compiler)
6
- // - [ ] fix allocating into actually global region
7
6
  // - [ ] fix exceptions on objects
8
7
  // - [X] allow omitting braces after `at` for singleton regions
9
8
 
@@ -35,12 +34,12 @@ def emptyBuffer[T](capacity: Int): Buffer[T] at {global} = {
35
34
  def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
36
35
  // TODO allocate buffer (and array) into a region r.
37
36
  val contents = array::allocate[T](initialCapacity)
38
- var head in global = 0
39
- var tail in global = 0
37
+ val head = ref(0)
38
+ val tail = ref(0)
40
39
 
41
40
  def size(): Int =
42
- if (tail >= head) { tail - head }
43
- else { initialCapacity - head + tail }
41
+ if (tail.get >= head.get) { tail.get - head.get }
42
+ else { initialCapacity - head.get + tail.get }
44
43
 
45
44
  def capacity(): Int = initialCapacity - size()
46
45
 
@@ -51,36 +50,36 @@ def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
51
50
  def read() = {
52
51
  if (buffer.empty?) None()
53
52
  else {
54
- val result: T = contents.unsafeGet(head);
55
- head = mod(head + 1, initialCapacity)
53
+ val result: T = contents.unsafeGet(head.get)
54
+ head.set(mod(head.get + 1, initialCapacity))
56
55
  Some(result)
57
56
  }
58
57
  }
59
58
  def write(el: T) = {
60
59
  if (buffer.full?) <> // raise(BufferOverflow())
61
60
 
62
- contents.unsafeSet(tail, el)
63
- tail = mod(tail + 1, initialCapacity)
61
+ contents.unsafeSet(tail.get, el)
62
+ tail.set(mod(tail.get + 1, initialCapacity))
64
63
  }
65
64
  }
66
65
  buffer
67
66
  }
68
67
  def refBuffer[T](): Buffer[T] at {global} = {
69
- var content: Option[T] in global = None()
68
+ val content = ref(None())
70
69
  new Buffer[T] {
71
- def capacity() = if (content.isEmpty) 1 else 0
72
- def full?() = content.isDefined
73
- def empty?() = isEmpty(content)
70
+ def capacity() = if (content.get.isEmpty) 1 else 0
71
+ def full?() = content.get.isDefined
72
+ def empty?() = isEmpty(content.get)
74
73
  def read() = {
75
- val res = content
76
- content = None()
74
+ val res = content.get
75
+ content.set(None())
77
76
  res
78
77
  }
79
- def write(el: T) = content match {
78
+ def write(el: T) = content.get match {
80
79
  case Some(v) =>
81
80
  <> // do raise(BufferOverflow(), "Cannot read element from buffer")
82
81
  case None() =>
83
- content = Some(el)
82
+ content.set(Some(el))
84
83
  }
85
84
  }
86
85
  }
@@ -88,24 +87,24 @@ def refBuffer[T](): Buffer[T] at {global} = {
88
87
  namespace examples {
89
88
  def main() = ignore[BufferOverflow] {
90
89
  // Buffer with capacity 1
91
- def b = emptyBuffer[Int](1);
92
- println(b.capacity);
93
- println(b.full?);
90
+ def b = emptyBuffer[Int](1)
91
+ println(b.capacity)
92
+ println(b.full?)
94
93
 
95
- b.write(17);
96
- println(b.read());
94
+ b.write(17)
95
+ println(b.read())
97
96
 
98
97
  // buffer with capacity 3
99
- def ringbuffer = emptyBuffer[Int](3);
100
- ringbuffer.write(1);
101
- ringbuffer.write(2);
102
- println(ringbuffer.read());
103
- ringbuffer.write(3);
104
- println(ringbuffer.read());
105
- println(ringbuffer.read());
106
- ringbuffer.write(4);
107
- ringbuffer.write(5);
108
- println(ringbuffer.read());
109
- println(ringbuffer.read());
98
+ def ringbuffer = emptyBuffer[Int](3)
99
+ ringbuffer.write(1)
100
+ ringbuffer.write(2)
101
+ println(ringbuffer.read())
102
+ ringbuffer.write(3)
103
+ println(ringbuffer.read())
104
+ println(ringbuffer.read())
105
+ ringbuffer.write(4)
106
+ ringbuffer.write(5)
107
+ println(ringbuffer.read())
108
+ println(ringbuffer.read())
110
109
  }
111
110
  }
@@ -25,82 +25,82 @@ def emptyQueue[T](): Queue[T] at {global} =
25
25
 
26
26
  def emptyQueue[T](initialCapacity: Int): Queue[T] at {global} = {
27
27
 
28
- var contents in global = array[Option[T]](initialCapacity, None())
29
- var head in global = 0
30
- var tail in global = 0
31
- var size in global = 0
32
- var capacity in global = initialCapacity
28
+ val contents = ref(array[Option[T]](initialCapacity, None()))
29
+ val head = ref(0)
30
+ val tail = ref(0)
31
+ val size = ref(0)
32
+ val capacity = ref(initialCapacity)
33
33
 
34
34
  def remove(arr: Array[Option[T]], index: Int): Option[T] = {
35
- with on[OutOfBounds].default { None() };
36
- val value = arr.get(index);
37
- arr.set(index, None());
35
+ with on[OutOfBounds].default { None() }
36
+ val value = arr.get(index)
37
+ arr.set(index, None())
38
38
  value
39
39
  }
40
40
 
41
41
  def nonEmpty[T] { p: => Option[T] / Exception[OutOfBounds] }: Option[T] =
42
- if (size <= 0) None() else on[OutOfBounds].default { None() } { p() }
42
+ if (size.get <= 0) None() else on[OutOfBounds].default { None() } { p() }
43
43
 
44
44
  // Exponential back-off
45
45
  def resizeTo(requiredSize: Int): Unit =
46
- if (requiredSize <= capacity) () else {
46
+ if (requiredSize <= capacity.get) () else {
47
47
  with on[OutOfBounds].ignore // should not happen
48
48
 
49
- val oldSize = capacity
50
- val newSize = capacity * 2
51
- val oldContents = contents
49
+ val oldSize = capacity.get
50
+ val newSize = capacity.get * 2
51
+ val oldContents = contents.get
52
52
  val newContents = array::allocate[Option[T]](newSize)
53
53
 
54
- if (head < tail) {
54
+ if (head.get < tail.get) {
55
55
  // The queue does not wrap around; direct copy is possible.
56
- copy(oldContents, head, newContents, 0, size) // changed tail to size
57
- } else if (size > 0) {
56
+ copy(oldContents, head.get, newContents, 0, size.get) // changed tail to size
57
+ } else if (size.get > 0) {
58
58
  // The queue wraps around; copy in two segments.
59
- copy(oldContents, head, newContents, 0, oldSize - head) // changed oldSize to oldSize - head
60
- copy(oldContents, 0, newContents, oldSize - head, tail) // changed oldSize - head to oldSize - head
59
+ copy(oldContents, head.get, newContents, 0, oldSize - head.get) // changed oldSize to oldSize - head
60
+ copy(oldContents, 0, newContents, oldSize - head.get, tail.get) // changed oldSize - head to oldSize - head
61
61
  }
62
62
 
63
- contents = newContents
64
- capacity = newSize
65
- head = 0
66
- tail = oldSize
63
+ contents.set(newContents)
64
+ capacity.set(newSize)
65
+ head.set(0)
66
+ tail.set(oldSize)
67
67
  }
68
68
 
69
69
  def queue = new Queue[T] {
70
- def empty?() = size <= 0
70
+ def empty?() = size.get <= 0
71
71
 
72
72
  def popFront() =
73
73
  nonEmpty {
74
- val result = contents.remove(head)
75
- head = mod(head + 1, capacity)
76
- size = size - 1
74
+ val result = contents.get.remove(head.get)
75
+ head.set(mod(head.get + 1, capacity.get))
76
+ size.set(size.get - 1)
77
77
  result
78
78
  }
79
79
 
80
80
  def popBack() =
81
81
  nonEmpty {
82
- tail = mod(tail - 1 + capacity, capacity)
83
- val result = contents.remove(tail)
84
- size = size - 1
82
+ tail.set(mod(tail.get - 1 + capacity.get, capacity.get))
83
+ val result = contents.get.remove(tail.get)
84
+ size.set(size.get - 1)
85
85
  result
86
86
  }
87
87
 
88
- def peekFront() = nonEmpty { contents.get(head) }
88
+ def peekFront() = nonEmpty { contents.get.get(head.get) }
89
89
 
90
- def peekBack() = nonEmpty { contents.get(tail) }
90
+ def peekBack() = nonEmpty { contents.get.get(tail.get) }
91
91
 
92
92
  def pushFront(el: T) = {
93
- resizeTo(size + 1);
94
- head = mod(head - 1 + capacity, capacity);
95
- size = size + 1;
96
- contents.unsafeSet(head, Some(el))
93
+ resizeTo(size.get + 1)
94
+ head.set(mod(head.get - 1 + capacity.get, capacity.get))
95
+ size.set(size.get + 1)
96
+ contents.get.unsafeSet(head.get, Some(el))
97
97
  }
98
98
 
99
99
  def pushBack(el: T) = {
100
- resizeTo(size + 1);
101
- contents.unsafeSet(tail, Some(el));
102
- size = size + 1;
103
- tail = mod(tail + 1, capacity)
100
+ resizeTo(size.get + 1)
101
+ contents.get.unsafeSet(tail.get, Some(el))
102
+ size.set(size.get + 1)
103
+ tail.set(mod(tail.get + 1, capacity.get))
104
104
  }
105
105
  }
106
106
  queue
@@ -109,30 +109,30 @@ def emptyQueue[T](initialCapacity: Int): Queue[T] at {global} = {
109
109
  namespace examples {
110
110
  def main() = {
111
111
  // queue with initial capacity 4
112
- def b = emptyQueue[Int](4);
113
- println(b.empty?);
114
- b.pushFront(1);
115
- b.pushBack(2);
116
- b.pushFront(3);
117
- b.pushBack(4);
112
+ def b = emptyQueue[Int](4)
113
+ println(b.empty?)
114
+ b.pushFront(1)
115
+ b.pushBack(2)
116
+ b.pushFront(3)
117
+ b.pushBack(4)
118
118
  // this will cause resizing:
119
- b.pushBack(5);
120
- b.pushBack(6);
121
- b.pushBack(7);
122
- b.pushBack(8);
119
+ b.pushBack(5)
120
+ b.pushBack(6)
121
+ b.pushBack(7)
122
+ b.pushBack(8)
123
123
  // and again:
124
- b.pushBack(9);
125
-
126
- println(b.empty?);
127
- println(b.popFront()); // Some(3)
128
- println(b.popFront()); // Some(1)
129
- println(b.popFront()); // Some(2)
130
- println(b.popFront()); // Some(4)
131
- println(b.popFront()); // Some(5)
132
- println(b.popFront()); // Some(6)
133
- println(b.popFront()); // Some(7)
134
- println(b.popFront()); // Some(8)
135
- println(b.popFront()); // Some(9)
136
- println(b.popFront()); // None()
124
+ b.pushBack(9)
125
+
126
+ println(b.empty?)
127
+ println(b.popFront()) // Some(3)
128
+ println(b.popFront()) // Some(1)
129
+ println(b.popFront()) // Some(2)
130
+ println(b.popFront()) // Some(4)
131
+ println(b.popFront()) // Some(5)
132
+ println(b.popFront()) // Some(6)
133
+ println(b.popFront()) // Some(7)
134
+ println(b.popFront()) // Some(8)
135
+ println(b.popFront()) // Some(9)
136
+ println(b.popFront()) // None()
137
137
  }
138
138
  }
@@ -30,6 +30,17 @@ effect stop(): Nothing
30
30
  def for[A] { stream: () => Unit / emit[A] } { action: A => Unit }: Unit =
31
31
  returning::for[A, Unit]{stream}{action}
32
32
 
33
+ /// Runs the stream as a message queue,
34
+ /// handling each of its elements that can produce new elements ("messages"),
35
+ /// until a fixed point is reached.
36
+ def fix[T] { one: T => Unit / emit[T] } { stream: => Unit / emit[T] }: Unit =
37
+ try {
38
+ stream()
39
+ } with emit[T] { value =>
40
+ fix[T] {one} { one(value) }
41
+ resume(())
42
+ }
43
+
33
44
  /// Turns a `list` into a producer of a push stream
34
45
  /// by emitting each contained value left-to-right.
35
46
  def each[A](list: List[A]): Unit / emit[A] =
@@ -14,7 +14,7 @@ def stringBuffer[A] { prog: => A / StringBuffer }: A = {
14
14
  var pos = 0
15
15
 
16
16
  def ensureCapacity(sizeToAdd: Int): Unit = {
17
- val cap = buffer.size - pos + 1
17
+ val cap = buffer.size - pos
18
18
  if (sizeToAdd <= cap) ()
19
19
  else {
20
20
  // Double the capacity while ensuring the required capacity
@@ -1,69 +1,79 @@
1
-
2
- // Common Runtime
3
- // --------------
4
- function Cell(init, region) {
5
- const cell = {
6
- value: init,
7
- backup: function() {
8
- const _backup = cell.value;
9
- // restore function (has a STRONG reference to `this`)
10
- return () => { cell.value = _backup; return cell }
11
- }
12
- }
13
- return cell;
1
+ // Complexity of state:
2
+ //
3
+ // get: O(1)
4
+ // set: O(1)
5
+ // capture: O(1)
6
+ // restore: O(|write operations since capture|)
7
+ const Mem = null
8
+
9
+ function Arena() {
10
+ const s = {
11
+ root: { value: Mem },
12
+ generation: 0,
13
+ fresh: (v) => {
14
+ const r = {
15
+ value: v,
16
+ generation: s.generation,
17
+ store: s,
18
+ set: (v) => {
19
+ const s = r.store
20
+ const r_gen = r.generation
21
+ const s_gen = s.generation
22
+
23
+ if (r_gen == s_gen) {
24
+ r.value = v;
25
+ } else {
26
+ const root = { value: Mem }
27
+ // update store
28
+ s.root.value = { ref: r, value: r.value, generation: r_gen, root: root }
29
+ s.root = root
30
+ r.value = v
31
+ r.generation = s_gen
32
+ }
33
+ }
34
+ };
35
+ return r
36
+ },
37
+ // not implemented
38
+ newRegion: () => s
39
+ };
40
+ return s
14
41
  }
15
42
 
16
- const global = {
17
- fresh: Cell
43
+ function snapshot(s) {
44
+ const snap = { store: s, root: s.root, generation: s.generation }
45
+ s.generation = s.generation + 1
46
+ return snap
18
47
  }
19
48
 
20
- function Arena(_region) {
21
- const region = _region;
22
- return {
23
- fresh: function(init) {
24
- const cell = Cell(init);
25
- // region keeps track what to backup, but we do not need to backup unreachable cells
26
- region.push(cell) // new WeakRef(cell))
27
- return cell;
28
- },
29
- region: _region,
30
- newRegion: function() {
31
- // a region aggregates weak references
32
- const nested = Arena([])
33
- // this doesn't work yet, since Arena.backup doesn't return a thunk
34
- region.push(nested) //new WeakRef(nested))
35
- return nested;
36
- },
37
- backup: function() {
38
- const _backup = []
39
- let nextIndex = 0;
40
- for (const ref of region) {
41
- const cell = ref //.deref()
42
- // only backup live cells
43
- if (cell) {
44
- _backup[nextIndex] = cell.backup()
45
- nextIndex++
46
- }
47
- }
48
- function restore() {
49
- const region = []
50
- let nextIndex = 0;
51
- for (const restoreCell of _backup) {
52
- region[nextIndex] = restoreCell() // new WeakRef(restoreCell())
53
- nextIndex++
54
- }
55
- return Arena(region)
56
- }
57
- return restore;
58
- }
59
- }
49
+ function reroot(n) {
50
+ if (n.value === Mem) return;
51
+
52
+ const diff = n.value
53
+ const r = diff.ref
54
+ const v = diff.value
55
+ const g = diff.generation
56
+ const n2 = diff.root
57
+ reroot(n2)
58
+ n.value = Mem
59
+ n2.value = { ref: r, value: r.value, generation: r.generation, root: n}
60
+ r.value = v
61
+ r.generation = g
60
62
  }
61
63
 
64
+ function restore(store, snap) {
65
+ // linear in the number of modifications...
66
+ reroot(snap.root)
67
+ store.root = snap.root
68
+ store.generation = snap.generation + 1
69
+ }
62
70
 
71
+ // Common Runtime
72
+ // --------------
63
73
  let _prompt = 1;
64
74
 
65
75
  const TOPLEVEL_K = (x, ks) => { throw { computationIsDone: true, result: x } }
66
- const TOPLEVEL_KS = { prompt: 0, arena: Arena([]), rest: null }
76
+ const TOPLEVEL_KS = { prompt: 0, arena: Arena(), rest: null }
67
77
 
68
78
  function THUNK(f) {
69
79
  f.thunk = true
@@ -80,9 +90,6 @@ function CAPTURE(body) {
80
90
 
81
91
  const RETURN = (x, ks) => ks.rest.stack(x, ks.rest)
82
92
 
83
- // const r = ks.arena.newRegion(); body
84
- // const x = r.alloc(init); body
85
-
86
93
  // HANDLE(ks, ks, (p, ks, k) => { STMT })
87
94
  function RESET(prog, ks, k) {
88
95
  const prompt = _prompt++;
@@ -90,13 +97,6 @@ function RESET(prog, ks, k) {
90
97
  return prog(prompt, { prompt, arena: Arena([]), rest }, RETURN)
91
98
  }
92
99
 
93
- function DEALLOC(ks) {
94
- const arena = ks.arena
95
- if (!!arena) {
96
- arena.length = arena.length - 1
97
- }
98
- }
99
-
100
100
  function SHIFT(p, body, ks, k) {
101
101
 
102
102
  // TODO avoid constructing this object
@@ -104,13 +104,15 @@ function SHIFT(p, body, ks, k) {
104
104
  let cont = null
105
105
 
106
106
  while (!!meta && meta.prompt !== p) {
107
- cont = { stack: meta.stack, prompt: meta.prompt, backup: meta.arena.backup(), rest: cont }
107
+ let store = meta.arena
108
+ cont = { stack: meta.stack, prompt: meta.prompt, arena: store, backup: snapshot(store), rest: cont }
108
109
  meta = meta.rest
109
110
  }
110
111
  if (!meta) { throw `Prompt not found ${p}` }
111
112
 
112
113
  // package the prompt itself
113
- cont = { stack: meta.stack, prompt: meta.prompt, backup: meta.arena.backup(), rest: cont }
114
+ let store = meta.arena
115
+ cont = { stack: meta.stack, prompt: meta.prompt, arena: store, backup: snapshot(store), rest: cont }
114
116
  meta = meta.rest
115
117
 
116
118
  const k1 = meta.stack
@@ -123,7 +125,8 @@ function RESUME(cont, c, ks, k) {
123
125
  let meta = { stack: k, prompt: ks.prompt, arena: ks.arena, rest: ks.rest }
124
126
  let toRewind = cont
125
127
  while (!!toRewind) {
126
- meta = { stack: toRewind.stack, prompt: toRewind.prompt, arena: toRewind.backup(), rest: meta }
128
+ restore(toRewind.arena, toRewind.backup)
129
+ meta = { stack: toRewind.stack, prompt: toRewind.prompt, arena: toRewind.arena, rest: meta }
127
130
  toRewind = toRewind.rest
128
131
  }
129
132