I\'m trying to understand Monad Transformers in Scala by porting some examples from this tutorial by Dan Piponi: http://blog.sigfpe.com/2006/05/grok-haskell-monad-transform
The problem is that the modify
you get with the usual imports is from State
, and isn't going to help you with StateT
.
It's a good idea to start with the Haskell type signature:
test3
:: (MonadState [Char] m, MonadState s (t m), MonadTrans t,
Num s) =>
t m (s, [Char])
Which you should be able to translate into something like this:
import scalaz._, Scalaz._
def test3[M[_]: Monad](implicit
inner: MonadState[({ type T[s, a] = StateT[M, s, a] })#T, String],
outer: MonadState[
({
type T[s, a] = StateT[({ type L[y] = StateT[M, String, y] })#L, s, a ]
})#T,
Int
],
mt: MonadTrans[({ type L[f[_], a] = StateT[f, Int, a] })#L]
) = for {
_ <- outer.modify(_ + 1)
_ <- mt.liftMU(inner.modify(_ + "1"))
a <- outer.get
b <- mt.liftMU(inner.get)
} yield (a, b)
It's hideous, but it's a fairly straightforward rewording of the Haskell. For some reason the compiler doesn't seem to find the outer
instance, though, so you have to help it a little:
def test3[M[_]: Monad](implicit
inner: MonadState[({ type T[s, a] = StateT[M, s, a] })#T, String],
mt: MonadTrans[({ type L[f[_], a] = StateT[f, Int, a] })#L]
) = {
val outer =
StateT.stateTMonadState[Int, ({ type L[y] = StateT[M, String, y] })#L]
for {
_ <- outer.modify(_ + 1)
_ <- mt.liftMU(inner.modify(_ + "1"))
a <- outer.get
b <- mt.liftMU(inner.get)
} yield (a, b)
}
Now you can write the following, for example:
scala> test3[Id].eval(0).eval("0")
res0: (Int, String) = (1,01)
Exactly as in the Haskell example.
You can clean this up a bit if you're happy with committing to Id
as the monad of the inner state transformer (as your comment suggests):
def test3 = {
val mt = MonadTrans[({ type L[f[_], a] = StateT[f, Int, a] })#L]
val outer = StateT.stateTMonadState[Int, ({ type L[y] = State[String, y] })#L]
for {
_ <- outer.modify(_ + 1)
_ <- mt.liftMU(modify[String](_ + "1"))
a <- outer.get
b <- mt.liftMU(get[String])
} yield (a, b)
}
It's a little less generic, but it may work for you.