Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 54 additions & 40 deletions libraries/js/effekt_runtime.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,60 @@
// Complexity of state:
//
// get: O(1)
// set: O(1)
// capture: O(1)
// restore: O(|write operations since capture|)

// Sentinel: a node whose .value is Mem is the current root.
const Mem = null

// reusable buffer for rerooting
const _rerootPath = []

function Arena() {
const s = {
root: { value: Mem },
generation: 0,
fresh: (v) => {
const r = {
value: v,
generation: s.generation,
store: s,
set: (v) => {
const s = r.store
const r_gen = r.generation
const s_gen = s.generation

if (r_gen == s_gen) {
r.value = v;
} else {
const root = { value: Mem }
// update store
s.root.value = { ref: r, value: r.value, generation: r_gen, root: root }
s.root = root
r.value = v
r.generation = s_gen
}
}
};
return r
},
// not implemented
newRegion: () => s
};
return s
/**
* A mutable reference inside an `Arena`.
*/
class Ref {
constructor(v, s) {
this.value = v;
this.generation = s.generation;
this.store = s;
}

set(v) {
const s = this.store;
const r_gen = this.generation;
const s_gen = s.generation;
if (r_gen === s_gen) {
this.value = v;
} else {
const root = { value: Mem };
s.root.value = { ref: this, value: this.value, generation: r_gen, root };

s.root = root;
this.value = v;
this.generation = s_gen;
}
}
}

/**
* A snapshottable arena: a bag of `Ref`s whose collective state can be
* captured in O(1) and restored in O(#writes since capture).
*/
class Arena {
constructor() {
this.root = { value: Mem };
this.generation = 0;
}

/**
* Allocate a new reference with initial value v.
*/
fresh(v) {
return new Ref(v, this);
}

/**
* Region support (not implemented!).
*/
newRegion() {
return this;
}
}

function snapshot(s) {
Expand Down Expand Up @@ -87,7 +101,7 @@ function restore(store, snap) {
let _prompt = 1;

const TOPLEVEL_K = (x, ks) => { throw { computationIsDone: true, result: x } }
const TOPLEVEL_KS = { prompt: 0, arena: Arena(), rest: null }
const TOPLEVEL_KS = { stack: null, prompt: 0, arena: new Arena(), rest: null }
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicitly setting stack: null seems to help V8 so that it knows it's always the same "type".
Same for the one created in RESET.


function THUNK(f) {
f.thunk = true
Expand All @@ -108,7 +122,7 @@ const RETURN = (x, ks) => ks.rest.stack(x, ks.rest)
function RESET(prog, ks, k) {
const prompt = _prompt++;
const rest = { stack: k, prompt: ks.prompt, arena: ks.arena, rest: ks.rest }
return prog(prompt, { prompt, arena: Arena([]), rest }, RETURN)
return prog(prompt, { stack: null, prompt, arena: new Arena(), rest }, RETURN)
}

function SHIFT(p, body, ks, k) {
Expand Down
Loading