如何在Scala的单子转换器堆栈上定义局部方法



我在Kleisli上面有一个单子变压器堆栈,定义为:

type Env = Map[String,Int]
type MyState = List[Int]
type S[A] = EitherT[StateT[WriterT[Kleisli[List,Env,?],String,?],MyState,?], String, A]

,我想定义一个local方法,签名如下:

def localE[A](f: Env => Env)(sa: S[A]): S[A] = ???

有可能吗?

我知道MonadReader中有一个local方法,签名为:

def local[A](f: R => R)(fa: F[A]): F[A]

所以最简单的解决方案是获得S隐含的MonadReader,然而,我找不到如何做到这一点。

我的一个简单的代码片段如下:

package examples
import cats._, data._
import cats.implicits._
object local {
 type Env = Map[String,Int]
 type MyState = List[Int]
 type S[A] = EitherT[StateT[WriterT[Kleisli[List,Env,?],String,?],MyState,?], String, A]
 // The following definition doesn't compile
 // implicit lazy val mr = MonadReader[S,Env]
 // Modify the environment
 def localE[A](f: Env => Env)(sa: S[A]): S[A] = ???
}

一个可能的解决方案是手动unlift monad堆栈。在本例中,我定义了以下两个辅助函数:

  def localW[A](f: Env => Env)
               (c: WriterT[Kleisli[List,Env,?],String,A]): 
         WriterT[Kleisli[List,Env,?],String,A] = {
   type WriterTF[F[_],A] = WriterT[F, String, A]
   val r: WriterT[Kleisli[List,Env,?],String,(String,A)] = 
      c.run.local(f).liftT[WriterTF]
   r.mapBoth { case (_, (w, x)) => (w, x) }
 }
   def localS[A](f: Env => Env)
                (c: StateT[WriterT[Kleisli[List,Env,?],String,?],MyState,A]): 
        StateT[WriterT[Kleisli[List,Env,?],String,?],MyState,A] = {
  StateT(s => localW(f)(c.run(s)))
  }

局部函数可以定义为:

  def localE[A](f: Env => Env)(sa: S[A]): S[A] = {
     EitherT(localS(f)(sa.value))
  }

最新更新