【问题标题】:Monad Transformer stacks in ScalaScala 中的 Monad Transformer 堆栈
【发布时间】:2014-05-03 10:13:47
【问题描述】:

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

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

【问题讨论】:

    标签: scala stack state monads either


    【解决方案1】:

    方法调用get[Int] 返回一个IndexedStateT[Id, Int, Int, Int]。您的Stack[Int] 扩展为IndexedStateT[Inner, Int, Int, Int],其中InnerEitherT[Id, String, A]。这有点难以推理,所以我会稍微简化一下你的例子。

    我们创建一个带有OptionStateT,而不是Inner 类型别名。

    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]
    

    如果您将其转换为示例代码,您需要像这样将lift State 转换为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
    }
    

    【讨论】:

      【解决方案2】:

      我开始写这篇文章是作为对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]
      

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

      【讨论】:

      • 我不知道 MonadState。棒极了!感谢分享。
      猜你喜欢
      • 2011-01-06
      • 2019-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-01
      • 1970-01-01
      相关资源
      最近更新 更多