DrBoolean/freeky

Implementation of State monad is possible?

safareli opened this issue · 8 comments

As I understand point of Free monad is to not write monad instance for our data structures, so why do we have chain defined on State?

This snippet is from an article:

data StateEff s x where
  Get :: StateEff s s
  Put :: s -> StateEff s ()

type EffState s = FFree (StateEff s)

getEff:: EffState s s
getEff = etaF Get

putEff:: s -> EffState s ()
putEff = etaF . Put

runEffState :: EffState s a -> s -> (a,s)
runEffState (FPure x) s     = (x,s)
runEffState (FImpure m q) s =
  let (x,s') = unEffState m s in runEffState (q x) s'

unEffState :: StateEff s a -> (s -> (a,s))
unEffState Get s     = (s,s)
unEffState (Put s) _ = ((),s)

It looks like state should be described as something like:

const State = daggy.taggedSum({
  Get: [],
  Put: ['v'],
})

State.get = liftF(State.Get)
State.put = v => liftF(State.Put(v))

But then I was screwed up with interpreter as currently foldMap is not passing any state from call to call, so I added this (something like this is also in scalaz Free)

Free.prototype.foldRun = function(interpreter, of, state) {
  return this.cata({
    Pure: a => of(a),
    Impure: (intruction_of_arg, next) => {
      const [nextm, nextState] = interpreter(intruction_of_arg)(state)
      return nextm.chain(result => next(result).foldRun(interpreter, of, nextState))
    }
  })
}

But still was not able to write proper interpreter for State.

how should we write interpreter for State? some example of it's usage will be awesome too.

Here I tried to use that, but i'm doing something wrong:

const Task = require('data.task')
const State = require('./state')
const {dispatch} = require('./interpret')

const stateToTask = s => state => {
  const unState = s.cata({
    Get: () => st => [st[0], st],
    Put: s => st => [null, [st[0], s]],
  })
  const [result, nextState] = unState(state)
  return [new Task((rej, res) => res(result)), nextState]
}


const runApp = dispatch([
  [State, stateToTask],
])


State.put(10).chain(
  _ => State.get.chain(
    a => State.put(20).chain(
      _ => State.get.map(b => b + a)
    )
  )
)
.foldRun(runApp, Task.of, [99, 99]).fork(console.error, console.log)

I came to this article and maybe we should defined FreerT and use it with actual State monad.

I think it might be useful, if we add foldRun so that Interpreter could carry some state around.

We could still reuse normal stateless transformers when foldRun-ing: [Either, stateless(eitherToTask)]
const stateless = f => a => acc => [f(a), acc]

We could also add Freer Transformer.

hey! Sorry i hadn't see this. Yeah, i believe you have to interpret w/o foldMap to keep state during the interpretation. But your knowledge is definitely beyond mine at this point so if you know of a better way, let's do that :)

I second the addition of foldRun.

I'm working on a couple of packages that use free monads. I wanted to use freeky for them, but ended up rolling my own implementation of Free, so I could hack together a way to pass state.

I'll try to PR if I can come up with a clean, working implementation.

Got a work-in-progress going on implementing State using foldRun: https://github.com/mkaemmerer/freeky/blob/state/example.js

Next step will be trying out a stateful example with multiple monads.

Love it!