Search code examples
scalamonadsscala-cats

Chain State monads with Scala Cats


I'm trying to chain a few sequential operations in a functional way with Scala and Cats. They look perfect separately but I'm not sure how can I chain them now with a flatMap / for comprehension.

So, let's say, I have something like

import cats.data.State

object Step1 {
    def apply() = State[String, Seq[String]] { text =>
        val ans = text.trim.split("""[\s]+""").toSeq
        (text, ans)
    }
}

println(Step1().run("Lorem Ipsum Dolor").value)

object Step2 {
    def apply() = State[Seq[String], Seq[String]] { terms =>
        val ans = terms.map(_.toLowerCase)
        (terms, ans)
    }
}

println(Step2().run(Seq("Lorem", "Ipsum", "Dolor")).value)

Ideally, I'd like to have something like

for {
    a <- Step1()
    b <- Step2()
} yield (b)

What is the best way to achieve this?


Solution

  • Take note of your types:

    For your Step1, you have State[String, Seq[String]]. For your Step2, you have State[Seq[String], Seq[String]].

    The function flatMap takes in an argument of M[A] and A => M[B] and returns M[B] but clearly your M[_] for Step1 and Step2 are clearly different even though they are both using the State datatype.

    Take note that State has a type signature of * -> * -> * or it looks something like State[S, A] where your S is your "state" and A is your value.

    In this case, if you really want to flatMap the two distinct State then you have to first "adjust" and equate the S of one of them.