Skip to content

Instantly share code, notes, and snippets.

@scorbiclife
Last active February 21, 2023 09:35
Show Gist options
  • Save scorbiclife/a336b958b259bb66bfa05ac5a18b02b2 to your computer and use it in GitHub Desktop.
Save scorbiclife/a336b958b259bb66bfa05ac5a18b02b2 to your computer and use it in GitHub Desktop.
Do Notation with For Loops
// I'm curious about your thoughts on using for loops for do-notation syntax.
// How does `idresultExpr`, `oresultExpr`, and `presultExpr` look?
// The identity monad
// `iterOfValue` is a generator function.
// it itself is a function, but it can return iterable iterators
function* iterOfValue<T>(x: T): Iterable<T> {
yield x;
}
for (const m of iterOfValue(3)) {
for (const n of iterOfValue(m)) {
console.log(n);
}
}
////////// this //////////
function* idresultExpr() {
for (const m of iterOfValue(3)) {
for (const n of iterOfValue(m)) {
yield n;
}
}
}
function valueOfIter<T>(ix: Iterable<T>): T {
for (const x of ix) {
return x;
}
throw new Error("Unreachable state from valid iterator");
}
const idresult = valueOfIter(idresultExpr());
console.log(idresult);
// The list monad
// does exactly what you think it would do so I'll just move on.
// The maybe monad
type Option<T> = { type: "some"; value: T } | { type: "none" };
const optionOf = {
some<T>(value: T): Option<T> {
return { type: "some", value };
},
none(): Option<never> {
return { type: "none" };
},
};
function* iterOfOption<T>(ox: Option<T>): Iterable<T> {
switch (ox.type) {
case "some": {
yield* iterOfValue(ox.value);
return;
}
case "none": {
return;
}
}
}
const ox = optionOf.some(1);
const oy = optionOf.some(2);
////////// this //////////
function* oresultExpr() {
for (const x of iterOfOption(ox)) {
for (const y of iterOfOption(oy)) {
yield x + y;
}
}
}
function optionOfIter<T>(ix: Iterable<T>): Option<T> {
for (const x of ix) {
return optionOf.some(x);
}
return optionOf.none();
}
const oresult = optionOfIter(oresultExpr());
console.log(oresult);
// future monad
// I had to use a for await loop...
// because I cannot put a normal for loop
// into a microtask queue by normal means
// also I'm only dealing with resolved promises for simplicity
// and because resolve/reject is irrelevant for this demonstration
// `iterOfPromise` is an asynchronous generator function.
// it itself is a function and is synchronous,
// but it returns an asynciterable asynciterator.
async function* aiterOfPromise<T>(px: Promise<T>): AsyncIterable<T> {
const x = await px;
yield x;
}
const px = Promise.resolve(1);
const py = Promise.resolve(2);
////////// this //////////
async function* presultExpr() {
for await (const x of aiterOfPromise(px)) {
for await (const y of aiterOfPromise(py)) {
yield x + y;
}
}
}
async function promiseOfAiter<T>(aix: AsyncIterable<T>): Promise<T> {
for await (const x of aix) {
return x;
}
throw new Error("Unreachable state from valid iterator");
}
const presult = promiseOfAiter(presultExpr());
presult.then(console.log);
@scorbiclife
Copy link
Author

scorbiclife commented Feb 21, 2023

  • Can I use this in production code?

    Please don't. I just wanted to see how the syntax looks for others.

  • What's different from callback hell?

    Functionally nothing, but I'm hoping it's easier to understand and reason about.
    I'm also hoping that it's more intuitive to think of doing this:

    function* ix(x) {
      yield x + 1;
    }
    
    function* iy(y) {
      yield y + 2;
    }
    
    function* ixy(i) {
      for (const xresult of ix(i)) {
        for (const yresult of iy(xresult)) {
          yield yresult;
        }
      }
    }
    for (const yresult of ixy(1)) {
      console.log(yresult);
    }

    than to think of doing this a la expressjs/cps/etc:

    function doX(x, next) {
      next(x + 1);
    }
    
    function doY(y, next) {
      next(y + 2);
    }
    
    function doXY(i, next) {
      doX(i, (xresult) =>
        doY(xresult, next));
    }
    
    doXY(1, console.log);

    kudos to express btw

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