freeky icon indicating copy to clipboard operation
freeky copied to clipboard

Implementation of State monad is possible?

Open safareli opened this issue 9 years ago • 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?

safareli avatar May 21 '16 20:05 safareli

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.

safareli avatar May 21 '16 21:05 safareli

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)

safareli avatar May 22 '16 17:05 safareli

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

safareli avatar May 22 '16 20:05 safareli

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.

safareli avatar May 22 '16 20:05 safareli

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 :)

DrBoolean avatar Oct 01 '16 17:10 DrBoolean

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.

  • https://github.com/mkaemmerer/electron-runner
  • https://github.com/mkaemmerer/filter-effects

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

mkaemmerer avatar Oct 17 '16 16:10 mkaemmerer

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.

mkaemmerer avatar Oct 17 '16 19:10 mkaemmerer

Love it!

DrBoolean avatar Oct 17 '16 20:10 DrBoolean