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!