简体   繁体   中英

How to implement Applicative for State[S, A] in scala / cats

In this session SystemFw gives an example of implementing State[S, A] wih vanilla scala, When follow the exmple, I run into a trouble in supplying a Applicative definition for the vanilla State type (In order to get the commands.traverse work. See code here

I tried to make a implicit def to solve the Applicative instance, but didn't figure out how to deal with the 2 type parameter.

How to implement Applicative for this type:

 case class State[S, A](modify: S => (A, S)) {

  def runA(initial: S): A = modify(initial)._1

  def flatMap[B](f: A => State[S, B]): State[S, B] =
    State { s =>
      val (result, nextState) = modify(s)
      f(result).modify(nextState)
    }
  }

Wrong code:

  implicit def stateApplicative[S, A]: Applicative[State[S, A]] = new Applicative[State[S, A]] {
    override def pure[A](x: A): State[A???] = ???   // error

    override def ap[A, B](ff: State[A => B])(fa: State[A???]): State[B] = ???   // error
  }

Basically, the solution to this problem is always fixing one type parameter.

In the case of a State you want the value inside the state to change but no the type of the state itself so you fix S .
So you can create an application for a given particular state, for example Int

type IntState[A] = State[A, Int]

implicit final val intStateApplicative: Applicative[IntState] =
  new Applicative[IntState] {
    // Some implementation.
  }

However, after you fill the implementation you will see that the fact of knowing that S was Int was meaningless. And that we could just copy and paste the whole code if S would have been String or whatever.
So, what we want is a way to say that this works for any S , we can do that with a type lambda (ie a function in the type level) .

type StateTypeFun[S] = { type F[A] = State[A, S] }

implicit final def stateApplicative[S]: Applicative[StateTypeFun[S]#F] =
  new Applicative[StateTypeFun[S]#F] {
    // Some implementation.
  }

And that is how we solve this problem.
Note that the type alias is unnecessary but makes the code easier to read, but you could have done Applicative[({ type F[A] = State[A, S]})#F] .


BTW, because this necessity of creating type lambdas is somewhat common in Scala 2 we have kind projector , and Scala 3 has proper syntax support for it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM