generators are dashed jolly spiffy
@bodil

ponies = [ "Applejack", "Fluttershy", "Pinkie Pie", "Rainbow Dash", "Rarity", "Twilight Sparkle" ];
ponies = { [Symbol.iterator]: () => { let c = 0; return { next: () => { c += 1; if (c === 1) return {value: "Pinkie Pie", done: false}; else if (c === 2) return {value: "Rainbow Dash", done: false}; else return {done: true}; } }; } };
infinity = { [Symbol.iterator]: () => { let c = 0; return { next: () => ({ value: c++, done: false }) }; } };
take = function(num, iterable) { return { [Symbol.iterator]: () => { let count = 0, it = iterable[Symbol.iterator](); return { next: () => (count++ < num) ? it.next() : {done: true} }; } }; }; infinity = { [Symbol.iterator]: () => { let c = 0; return { next: () => ({ value: c++, done: false }) }; } };
map = function(fn, iterable) { return { [Symbol.iterator]: () => { const it = iterable[Symbol.iterator](); return { next: () => { const next = it.next(); return next.done ? next : { done: false, value: fn(next.value) }; } }; } }; }; take = function(num, iterable) { return { [Symbol.iterator]: () => { let count = 0, it = iterable[Symbol.iterator](); return { next: () => (count++ < num) ? it.next() : {done: true} }; } }; }; infinity = { [Symbol.iterator]: () => { let c = 0; return { next: () => ({ value: c++, done: false }) }; } };

Exercise 1!

Write a filter function!


&filter(i => i > 2, take(6, infinity));
// 3 ⇨ 4 ⇨ 5
        
// Exercise 1 map = function(fn, iterable) { return { [Symbol.iterator]: () => { const it = iterable[Symbol.iterator](); return { next: () => { const next = it.next(); return next.done ? next : { done: false, value: fn(next.value) }; } }; } }; }; take = function(num, iterable) { return { [Symbol.iterator]: () => { let count = 0, it = iterable[Symbol.iterator](); return { next: () => (count++ < num) ? it.next() : {done: true} }; } }; }; infinity = { [Symbol.iterator]: () => { let c = 0; return { next: () => ({ value: c++, done: false }) }; } };
infinity = { [Symbol.iterator]: () => { let c = 0; return { next: () => ({ value: c++, done: false }) }; } };
map = function*(fn, iter) { let next; while(!(next = iter.next()).done) { yield fn(next.value); } }; take = function*(num, iter) { let c = 0, next; while (c++ < num && !(next = iter.next()).done) { yield next.value; } }; infinity = function*() { let c = 0; while (true) { yield c++; } };

Exercise 2!

Write the filter function using generators!


&filter(i => i > 2, take(6, infinity()));
// 3 ⇨ 4 ⇨ 5
        
// Exercise 2 map = function*(fn, iter) { let next; while (!(next = iter.next()).done) { yield fn(next.value); } }; take = function*(num, iter) { let c = 0, next; while (c++ < num && !(next = iter.next()).done) { yield next.value; } }; infinity = function*() { let c = 0; while (true) { yield c++; } };
fiveup = function*() { let c = 0; while (true) { c = yield c; c = c + 5; } };
unit = v => new Promise((res) => res(v));
run = (iter, val) => { const next = iter.next(val); if (!next.done) { next.value.then(result => run(iter, result)); } };
run = (iter, val) => { const next = iter.next(val); if (!next.done) { next.value.then(result => run(iter, result)); } };

Exercise 3!

Make run handle errors!


&run(fetchText("http://horse.ebooks/"));
// TypeError: Failed to fetch

Hint: Promise.then(successFn, errorFn)
      Deferred.reject(error)
        
// Exercise 3 run = (iter, val, done = Promise.defer()) => { const next = iter.next(val); if (!next.done) { next.value.then(result => run(iter, result, done)); } else { done.resolve(next.value); } return done.promise; }; fetchText = function*(url) { return yield (yield fetch(url)).text(); };
ponies = { pie: "Pinkie Pie", dash: "Rainbow Dash" }; things = function*() { const dash = ponies.dash; const pie = ponies.pie; return dash + " is friends with " + pie; } run = (iter, val, done = Promise.defer()) => { const next = iter.next(val); if (!next.done) { next.value.then( result => run(iter, result, done), error => done.reject(error) ); } else { done.resolve(next.value); } return done.promise; };

Exercise 4!

Make maybe report errors!

&run(pie());
// "Rainbow Dash is friends with Pinkie Pie"

&run(twi());
// Error: undefined key: twi
// Exercise 4 ponies = { pie: "Pinkie Pie", dash: "Rainbow Dash" }; maybe = (val) => ({ then: (fn) => val != null ? fn(val) : null }); prop = (key, obj) => maybe(obj[key]); pie = function*() { const dash = yield prop("dash", ponies); const pie = yield prop("pie", ponies); return dash + " is friends with " + pie; } twi = function*() { const dash = yield prop("dash", ponies); const twi = yield prop("twi", ponies); return dash + " is friends with " + twi; } run = (iter, val, done = Promise.defer()) => { const next = iter.next(val); if (!next.done) { next.value.then( result => run(iter, result, done), error => done.reject(error) ); } else { done.resolve(next.value); } return done.promise; };

The Interface

interface Async<A> {

  then: (f: (a: A) => Async<B>) => Async<B>;

} 

Let's Add a Wrapper

interface Async<A> {

  unit: (value: A) => Async<A>;

  then: (f: (a: A) => Async<B>) => Async<B>;

} 

Let's Review

Let's Review

We can do this with anything which has a then method, not just promises!

Imagine a World

Without Callbacks

run(function*() {
  const a = yield fs.readFile("a.txt");
  const b = yield fs.readFile("b.txt");
  yield fs.writeFile("both.txt", a + b);
}); 

Just Like Haskell

main = do
  a <- readFile "a.txt"
  b <- readFile "b.txt"
  writeFile "both.txt" (a ++ b) 

Haskell is All Callbacks

They were here 25 years ago.

function joinFiles(f1, f2, f3, callback) {
  fs.readFile(f1, function(err, a) {
    if (err) return callback(err);
    fs.readFile(f2, function(err, b) {
      if (err) return callback(err);
      fs.writeFile(f3, a + b, callback);
    });
  });
}

until one man proposed a solution

MONADS

This Is a Monad

class Monad m where

  return :: a -> m a

  (>>=) :: m a -> (a -> m b) -> m b 

This Is a Monad

interface Monad<A> {

  unit: (value: A) => Monad<A>;

  then: (f: (a: A) => Monad<B>) => Monad<B>;

} 

You Have Seen

This Is Monad Comprehension

main = do
  a <- readFile "a.txt"
  b <- readFile "b.txt"
  writeFile "both.txt" (a ++ b) 

This Is Monad Comprehension

run(function*() {
  const a = yield fs.readFile("a.txt");
  const b = yield fs.readFile("b.txt");
  yield fs.writeFile("both.txt", a + b);
}); 

The Three Monad Laws

// §1. Left identity:
      unit(a).then(f) ≡ unit(f(a));

// §2. Right identity:
         m.then(unit) ≡ m;

// §3. Associativity:
    m.then(f).then(g) ≡ m.then(a => g(f(a))); 

The Co library

https://github.com/tj/co

that is all
@bodil — http://bodil.lol/generators/