代码之家  ›  专栏  ›  技术社区  ›  user3459840

Scala中的Monad Transformer堆栈

  •  4
  • user3459840  · 技术社区  · 10 年前

    我正在学习Scala中的monad转换器,但我遇到了一个迄今为止无法解决的问题。在我的monad转换器堆栈中,我组成了非此即彼和状态monad。但是,我无法调用属于两个monad之一的函数:

    import scalaz._
    import Scalaz._
    
    object Minimal {
      type Inner[A] = EitherT[Id, String, A]
      type Outer[F[+_], A] = StateT[F,Int,A]
      type Stack[A] = Outer[Inner, A]
    
      def foo:Stack[Int] = for {
        n <- get[Int]
      } yield {
        2 * n
      }
    
      def main(args: Array[String]): Unit = {
        val x = foo.eval(8)
        println(x)
      }
    }
    

    失败,并显示以下错误消息:

    [error] Minimal.scala:10: type mismatch;
    [error]  found   : scalaz.IndexedStateT[scalaz.Id.Id,Int,Int,Int]
    [error]  required: Minimal.Stack[Int]
    [error]     (which expands to)  scalaz.IndexedStateT[Minimal.Inner,Int,Int,Int]
    [error]     n <- get[Int]
    

    如果我将monad转换器堆栈更改为:

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

    程序编译和运行没有问题。有人知道我做错了什么吗?

    2 回复  |  直到 10 年前
        1
  •  5
  •   EECOLOR    10 年前

    方法调用 get[Int] 返回一个 IndexedStateT[Id, Int, Int, Int] 你的 Stack[Int] 扩展到 IndexedStateT[Inner, Int, Int, Int] 哪里 Inner 是一个 EitherT[Id, String, A] 。这有点难以理解,所以我将稍微简化一下您的示例。

    而不是 内部的 键入别名,我们将创建 StateT 使用 Option .

    type Stack[A] = StateT[Option, Int, A]
    

    分配 获取[Int] 仍然会失败。

    val x:Stack[Int] = get[Int]
    //type mismatch; 
    //  found : scalaz.State[Int,Int]
    //    (which expands to) scalaz.IndexedStateT[scalaz.Id.Id,Int,Int,Int]
    //  required: Minimal.Stack[Int] 
    //    (which expands to) scalaz.IndexedStateT[Option,Int,Int,Int]
    

    为了解决这个问题,我们需要 lift 变压器至 选项 :

    val x:Stack[Int] = get[Int].lift[Option]
    

    如果将其转换为示例代码,则需要 举起 这个 State 内部的 这样地。请注意,您需要更改 内部的 也是协变的:

    type Inner[+A] = EitherT[Id, String, A]
    type Stack[A] = StateT[Inner, Int, A]
    
    val x:Stack[Int] = get[Int].lift[Inner]
    

    为了能够在不手动提升的情况下编写此代码,可以引入隐式转换。完整示例:

    type Inner[+A] = EitherT[Id, String, A]
    type Outer[F[+_], A] = StateT[F, Int, A]
    type Stack[A] = Outer[Inner, A]
    
    implicit def liftToStack[A](x:Outer[Id, A]):Stack[A] = x.lift[Inner]
    
    def foo: Stack[Int] = for {
      n <- get[Int]
    } yield {
      2 * n
    }
    
        2
  •  5
  •   Community datashaman    7 年前

    我开始写这篇评论 EECOLOR 的答案(我刚刚投了赞成票,除了最后的隐式转换外,我还推荐了这个答案),但它有点笨拙,所以这里有一个新的答案。

    EECOLOR的诊断完全正确,但 MonadState (我在 my answer to your other question 今天早上)可以让你避免明显的举重。例如,您可以编写以下内容:

    import scalaz._, Scalaz._
    
    type Inner[+A] = EitherT[Id, String, A]
    type Stack[S, +A] = StateT[Inner, S, A]
    
    def foo: Stack[Int, Int] = for {
      n <- MonadState[Stack, Int].get
    } yield 2 * n
    

    注意(正如我前面的问题)我已经改变了 Stack 要根据状态类型进行参数化。您可以很容易地将其更改为以下内容:

    type MyState[S, +A] = StateT[Inner, S, A]
    type Stack[+A] = MyState[Int, A]
    

    如果要捕获堆栈中的状态类型。