stacking StateT in scalaz

后端 未结 1 1203
暖寄归人
暖寄归人 2021-01-17 13:12

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

相关标签:
1条回答
  • 2021-01-17 14:12

    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.

    0 讨论(0)
提交回复
热议问题