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-transformers.html
I did a couple of easy ones:
import Control.Monad.State
import Control.Monad.Identity
test1 = do
a <- get
modify (+1)
b <- get
return (a,b)
test2 = do
a <- get
modify (++"1")
b <- get
return (a,b)
go1 = evalState test1 0
go2 = evalState test2 "0"
becomes:
import scalaz._, Scalaz._
val test1 = for {
a <- get[Int]
_ <- modify[Int](1+)
b <- get
} yield (a,b)
val test2 = for {
a <- get[String]
_ <- modify[String](_ + "1")
b <- get
} yield (a,b)
val go1 = test1.eval(0)
val go2 = test2.eval("0")
But how the heck can I port this next example to Scala?
test3 = do
modify (+ 1)
lift $ modify (++ "1")
a <- get
b <- lift get
return (a,b)
go3 = runIdentity $ evalStateT (evalStateT test3 0) "0"
I've gotten this far using scalaz 7.1.0-M6:
type SST[F[_],A] = StateT[F,String,A]
type IST[F[_],A] = StateT[F,Int,A]
val p1: StateT[Id,Int,Unit] = modify[Int](1+)
val p2: StateT[Id,String,Unit] = modify[String](_ + "1")
val p3: StateT[({type l[a]=StateT[Id,String,a]})#l,Int,Unit] = p2.liftM[IST]
but that's not even close yet, and may even be backwards for all I can tell.
Of course I can do this:
import scalaz.Lens._
val test3 = for {
_ <- firstLens[Int,String] lifts (modify (1+))
_ <- secondLens[Int,String] lifts (modify (_ + "1"))
a <- firstLens[Int,String] lifts get
b <- secondLens[Int,String] lifts get
} yield (a,b)
val go3 = test3.eval(0,"0")
but then I'm not using stacked StateT at all, so it doesn't answer the question.
Thanks in advance!
A stack is a data structure that follows the last-in, first-out (LIFO) principle. We can add or remove element only from one end called top. Scala has both mutable and immutable versions of a stack. import scala.collection.mutable.Stack var s = Stack [type] () // OR var s = Stack (val1, val2, val3, ...)
Welcome to Scalaz 7.3 Scalaz is a Scala library for functional programming. It provides purely functional data structures to complement those from the Scala standard library. It defines a set of foundational type classes (e.g. Functor, Monad) and corresponding instances for a large number of data structures. Getting started
Scala has both mutable and immutable versions of a stack. import scala.collection.mutable.Stack var s = Stack [type] () // OR var s = Stack (val1, val2, val3, ...) Once stack has been created we can either push elements to the stack or pop them out of the stack.
Scalaz is designed and developed by Scalaz contributors View on GitHub © 2018 Scalaz Maintainers
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With