4

我正在学习 Scala 中的 monad 转换器,但我遇到了一个我认为目前无法解决的问题。在我的 monad 转换器堆栈中,我组成了 Either 和 State 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]

该程序编译并运行没有问题。有人知道我在这里做错了什么吗?

4

2 回答 2

5

方法调用get[Int]返回一个IndexedStateT[Id, Int, Int, Int]. 你Stack[Int]扩展到IndexedStateT[Inner, Int, Int, Int]where Inneris an EitherT[Id, String, A]。这有点难以推理,所以我将简化您的示例。

我们用.代替Inner类型别名。StateTOption

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

的分配get[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将变压器转换为Option

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

如果您将其转换为您的示例代码,liftState需要Inner像这样。请注意,您还需要将您的定义更改Inner为协变:

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
}
于 2014-03-25T20:42:27.293 回答
5

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

EECOLOR 的诊断是完全正确的,但是MonadState(我今天早上在回答您的另一个问题时使用了它)可以让您避免显式提升。例如,您可以编写以下内容:

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]

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

于 2014-03-25T21:37:40.723 回答