【问题标题】:Scala type inference not working with generic case class and lambdaScala 类型推断不适用于通用案例类和 lambda
【发布时间】:2016-06-03 02:02:24
【问题描述】:

我正在尝试将函数:A => B 应用于 Result[A] 以获取 Result[B] 并收到奇怪的类型错误。

 found   : Product with Serializable with this.Result[_ <: B]
 required: this.Result[B]

我不确定如何解决此问题。看起来 scala 正在尝试将函数的输出转换为字符串(可序列化),因为它找不到 Success/Failure 和 Result 之间的转换?

简化代码如下。我尝试附加显式类型信息,但代码无法编译。任何帮助将不胜感激!

sealed trait Result[A]{ 
    def map[B](f: A => B) = this match{
        case Success((a, rest)) => Success((f(a), rest))
        case Failure(m) => Failure(m)
    }
}
case class Success[A](result: (A, List[Char])) extends Result[A]
case class Failure[A](message: String) extends Result[A]

object Utils{
    def map[A,B](r: Result[A], f: A => B):Result[B] = {
        r.map(f)
    }
}

【问题讨论】:

    标签: scala generics types lambda


    【解决方案1】:

    解释原来的问题:你没有指定Result.map的返回类型,所以Scala必须推断它。为此,它会查看两个分支。第一个的返回类型是Success[B]。第二个的返回类型是Failure[Nothing],因为Scala 没有理由假设你的意思是Failure[B](m)

    所以现在它必须找到Success[B]Failure[Nothing] 都适合的类型(它们的最小上限)。好吧,它们都扩展了ProductSerializable(因为它们是案例类)。它们也都扩展了Result,但具有不同的类型参数。 NothingB 的最小上限是 B,因此它们都扩展了 Result[_ &lt;: B]。这就是你最终选择的类型map:Product with Serializable with Result[_ &lt;: B]

    请注意,通过应用 Andy Hayden 的答案,您将得到 Product with Serializable with Result[B],而不是 不是 Result[B],这可能是您想要的。为此,要么明确指定返回类型(即使没有协方差也能工作,希望你现在能明白原因),或者使 Result 扩展 Product with Serializable

    【讨论】:

      【解决方案2】:

      正如Alexey所指出的,你应该指定Result的map的返回类型(否则Failure类型是不明确的,虽然你可以指定Failure类型而不是Failure[B]):

      sealed trait Result[A]{ 
          // specify the return type Result[B]
          def map[B](f: A => B): Result[B] = this match{
              case Success((a, rest)) => Success((f(a), rest))
              case Failure(m) => Failure(m)  // alternatively use Failure[B](m) here
          }
      }
      case class Success[A](result: (A, List[Char])) extends Result[A]
      case class Failure[A](message: String) extends Result[A]
      
      object Utils{
          def map[A,B](r: Result[A], f: A => B):Result[B] = {
              r.map(f)
          }
      }
      

      这将为失败提供正确的类型:

      scala> val f = Failure[Int]("oops")
      Failure("oops"): Failure[Int]
      
      scala> Utils.map(f, {i: Int => 4})
      Failure("oops"): Result[Int]
      

      vs(使用下面的协变解决方案):

      scala> Utils.map(f, {i: Int => 4})
      Failure(oops): Product with Serializable with Result[_37] forSome { type _37 <: Int }
      

      这不是你想要的!


      最初我建议你让你的类型协变(+A):

      sealed trait Result[+A]{ 
          def map[B](f: A => B) = this match{
              case Success((a, rest)) => Success((f(a), rest))
              case Failure(m) => Failure(m)
          }
      }
      case class Success[A](result: (A, List[Char])) extends Result[A]
      case class Failure[A](message: String) extends Result[A]
      
      object Utils{
          def map[A,B](r: Result[A], f: A => B):Result[B] = {
              r.map(f)
          }
      }
      

      更多讨论请见this great blogpost

      引用部分帖子:

      子类型关系 假设 Orange 类扩展 Fruit 成立。
      如果声明了Box[A] 类,那么A 可以以+- 为前缀。

      没有注解的A是不变的,即:
      Box[Orange]Box[Fruit]没有继承关系。

      +A 是协变的,即: Box[Orange]Box[Fruit] 的子类型。
      var f: Box[Fruit] = new Box[Orange]() 是允许的。

      -A 是逆变的,即:
      Box[Fruit]Box[Orange] 的子类型。
      var f: Box[Orange] = new Box[Fruit]() 是允许的。

      【讨论】:

      • 我知道它与协变和逆变有关。我是 scala 的新手,我仍然不清楚。非常感谢,这篇博文非常有帮助。
      • “基本上这告诉 scala Success[B] 和 Failure[B] 是 Result[B] 的子类型。”这是完全错误的,Success[A](...) extends Result[A] 确实那个。您还应该同时使 SuccessFailure 成为协变的。
      猜你喜欢
      • 2020-07-20
      • 2020-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-05
      • 1970-01-01
      • 2016-04-13
      相关资源
      最近更新 更多