Skip to content

Instantly share code, notes, and snippets.

@conartist6
Last active Aug 4, 2022
Embed
What would you like to do?
State machine with generators
/*
Lamps often have on-off switches that twist clockwise, clicking as they do.
Some such switches have four states, even though the lamp has only two (on, and off).
On these lamps clicking the switch will only cause an on/off transition every other click.
Just looking at the lamp you'll know if it's on, but not if the next click will turn it off.
To reliably interact with such a lamp you will use an algorithm, even in real life.
I use the term strategy to refer to an algorithm expressed with generators like this --
the strategy pattern is useful to abstract an algorithm from the representation of the data.
*/
function* toggleStrategy(lamp) {
const wasOn = lamp.on;
while (lamp.on === wasOn) {
yield 'click';
}
}
assertEqual(2, useLamp(toggleStrategy, 0b00));
assertEqual(1, useLamp(toggleStrategy, 0b01));
assertEqual(2, useLamp(toggleStrategy, 0b10));
assertEqual(1, useLamp(toggleStrategy, 0b11));
function* onStrategy(lamp) {
while (!lamp.on) {
yield 'click';
}
}
assertEqual(2, useLamp(onStrategy, 0b00));
assertEqual(1, useLamp(onStrategy, 0b01));
assertEqual(0, useLamp(onStrategy, 0b10));
assertEqual(0, useLamp(onStrategy, 0b11));
function useLamp(strategy, initialState) {
const facade = ({ get on() { return state >= 0b10 }});
// `co` is the strategy generator, which is used as a coroutine
const co = strategy(facade);
let state = initialState;
let clicks = 0;
// run the strategy generator up to the first yield
let step = co.next();
while (!step.done) {
const action = step.value;
switch(action) {
case 'click':
++clicks;
state = (state + 0b01) & 0b11;
break;
default:
throw new Error(`unknown {action: ${action}}`);
}
// run the strategy generator until the next yield
step = co.next();
}
return clicks;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment