stacking StateT in scalaz

心已入冬 提交于 2019-12-01 15:25:31

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.

Footnote

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!