DrBoolean / freeky

Free monad Collection
MIT License
176 stars 15 forks source link

result of Monad.do is not reusable #4

Closed safareli closed 8 years ago

safareli commented 8 years ago

Monad.do creates generator calles next on it and returns firs ocurance of Free monad. When we call foldMap on that it will drain generator as it should, but issue is that we could not reuse result of monad.do

For example if we dublicate example.js:41 it will fail, as both are useing one generator.

app.foldMap(runApp, Task.of).fork(console.error, console.log)
+ app.foldMap(runApp, Task.of).fork(console.error, console.log)
/Users/safareli/dev/freeky/free.js:42
        next(result).foldMap(interpreter, of))
                    ^
TypeError: Cannot read property 'foldMap' of undefined

I have fixed this but not in the cleanest way here it is:

const Free = require('./free').Free

const step = (g) => (value) => {
  const result = g.next(value)
  return result.done ?
        result.value :
        result.value.chain(step(g))
}

const Monad = {
  do: gen => {
    return Object.keys(Free.prototype).reduce((result, key) => {
      result[key] = function(){
        const m = step(gen())(undefined)
        return m[key].apply(m,arguments)
      }
      return result;
    }, {});
  }
  of: Free.of,
}

module.exports = Monad

the key here is that instead of instantiating generator and getting first item from it we create Free like object which will create generators on each call so we could reuse that

safareli commented 8 years ago

We could get first Monad from generator to remove dependence on Free:

const step = (g) => (value) => {
  const result = g.next(value)
  return result.done
    ? result.value
    : result.value.chain(step(g))
}

const runGenerator = (gen) => {
  const firstM = step(gen())(undefined)
  for (let key of Object.keys(firstM.constructor.prototype)){
    firstM[key] = (...args) => {
      const m = step(gen())(undefined);
      return m[key](...args)
    }
  }
  return firstM
}

const Monad = {
  do: runGenerator
}

module.exports = Monad
DrBoolean commented 8 years ago

Right on! Thanks for helping with this. I'm going to be spending some time on it over the weekend so I'll look it over for sure.

safareli commented 8 years ago

Welcome!

I really like that idea of using generators as haskell do like syntax and it will be nice if we could have it so that it is not tied to particular object and could be useful with anything that has chain.

But:

Monad.do(function* () {
  let a = fib(100000000);
  let b = yield Get('/fib='+a);
  result Done(b + 1);
});

As we could not clone iterator it will not work for all monads :/ . I think if we use sweet.js to write a macro for do will be nice

safareli commented 8 years ago

Actually there is a macro for do

DrBoolean commented 8 years ago

@safareli thanks for pointing this out. I was inspired by bodil's library https://github.com/bodil/eulalie/blob/master/src/index.js#L268 but I'd rather have uniformity and predictability rather than a fancy new syntax.

In my experience, I've found it way too easy to cheat this way and sneak in imperative one liners instead of something composable. I suppose we should axe it and stick with chain

safareli commented 8 years ago

Agreed, we should use chain Instead of generators.

safareli commented 8 years ago

I'll close this issue es there is no way to fix it.

safareli commented 8 years ago

As of in Fluture/issues/10 I'm reopening this issue.