Skip to content

Instantly share code, notes, and snippets.

@Lucifier129
Created April 23, 2020 02:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Lucifier129/708f17339b1fd52fb9d01110a7e895a0 to your computer and use it in GitHub Desktop.
Save Lucifier129/708f17339b1fd52fb9d01110a7e895a0 to your computer and use it in GitHub Desktop.
poor man's algebraic-effects via throw + hand-written continuations
let run = (f, handlers) => {
try {
return f();
} catch (effect) {
if (effect.type in handlers) {
handlers[effect.type](effect.payload, effect.continuation);
} else {
throw effect
}
}
};
let async = (f) => (...args) => {
let handlers = {
await(fetcher, k) {
fetcher(async(k))
},
};
run(() => f(...args), handlers);
};
let await = (fetcher) => (next) => {
throw {
type: "await",
payload: fetcher,
continuation: next,
};
};
let db = {
a: {
data: "a",
},
b: {
data: "b",
},
};
let fetchAllData = () => k => {
setTimeout(() => {
Object.values(db).forEach(k)
}, 100)
}
let testMultShot = async(() => {
await(fetchAllData())
(data => {
console.log('multi-shot', data)
})
})
let fetchData = (id) => (k) => {
setTimeout(() => {
k(db[id]);
}, 100);
};
let main = async((id) => {
await(fetchData(id))
(data => {
console.log("data", data);
});
});
let test = async(() => {
await(fetchData('a'))
(a => {
console.log('a', a)
await(fetchData('b'))
(b => {
console.log('b', b)
await(fetchData('a'))
(c => {
console.log('c', c)
})
})
})
})
main('a')
main('b')
test()
testMultShot()
@Lucifier129
Copy link
Author

Lucifier129 commented Apr 23, 2020

RoundRobinScheduler

let run = (f, handlers) => {
  try {
    return f();
  } catch (effect) {
    if (effect.type in handlers) {
      handlers[effect.type](effect.payload, effect.continuation);
    } else {
      throw effect;
    }
  }
};

let fork = (f) => {
  let then = (k) => {
    throw {
      type: 'fork',
      payload: f,
      continuation: k,
    };
  };
  return { then };
};
let yield = () => {
  let then = (k) => {
    throw {
      type: 'yield',
      payload: void 0,
      continuation: k,
    };
  };

  return { then };
};

let runSchedular = (main) => {
  let queue = [];

  let enqueue = (ff) => {
    queue.push(() => spawn(ff));
  };

  let dequeue = () => {
    if (queue.length) {
      queue.shift()();
    }
  };

  let handlers = {
    fork: (forked, k) => {
      enqueue(k);
      spawn(forked);
    },
    yield: (_, k) => {
      enqueue(k);
      dequeue();
    },
  };

  let spawn = (f) => {
    run(f, handlers);
    dequeue();
  };

  return spawn(main);
};

function f(id, depth) {
  console.log('Starting number %i', id);

  if (depth > 0) {
    console.log('Forking number %i', id * 2 + 1);

    fork(() => f(id * 2 + 1, depth - 1)).then(() => {
      console.log('Forking number %i', id * 2 + 2);
      fork(() => f(id * 2 + 2, depth - 1)).then(() => {
        console.log('Finishing number %i', id);
      });
    });
  } else {
    console.log('Yielding in number %i', id);

    yield().then(() => {
      console.log('Resumed number %i', id);
      console.log('Finishing number %i', id);
    });
  }
}

runSchedular(() => f(0, 2));

/**
 * output:
 * Starting number 0
    Forking number 1
    Starting number 1
    Forking number 3
    Starting number 3
    Yielding in number 3
    Forking number 2
    Starting number 2
    Forking number 5
    Starting number 5
    Yielding in number 5
    Forking number 4
    Starting number 4
    Yielding in number 4
    Resumed number 3
    Finishing number 3
    Finishing number 0
    Forking number 6
    Starting number 6
    Yielding in number 6
    Resumed number 5
    Finishing number 5
    Finishing number 1
    Resumed number 4
    Finishing number 4
    Finishing number 2
    Resumed number 6
    Finishing number 6
 */

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment