【问题标题】:"Missing parameter type" in for-comprehension when overloading flatMap重载flatMap时理解中的“缺少参数类型”
【发布时间】:2017-01-02 01:42:40
【问题描述】:

我编写了自己的名为 Maybe 的类 Either 的 monad 类,其中包含一个值或一个错误对象。我希望此类的对象与Future 结合,这样我就可以将Maybe[Future[T], E]] 变成Future[Maybe[T, E]]。因此我实现了两个flatMap 方法:

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

sealed abstract class Maybe[+E, +V] {

  def map[W](f: V ⇒ W ): Maybe[E, W] = this match {
    case Value(v) ⇒ Value(f(v))
    case Error(_) ⇒ this.asInstanceOf[Error[E, W]]
  }

  def flatMap[F >: E, W](f: V ⇒ Maybe[F, W]): Maybe[F, W] = this match {
    case Value(v) ⇒ f(v)
    case Error(_) ⇒ this.asInstanceOf[Error[F, W]]
  }

  def flatMap[W](f: V ⇒ Future[W]): Future[Maybe[E, W]] = this match {
    case Value(v) ⇒ f(v).map(Value(_))
    case Error(_) ⇒ Future.successful(this.asInstanceOf[Error[E, W]])
  }
}

final case class Value[+E, +V](value: V) extends Maybe[E, V]    
final case class Error[+E, +V](error: E) extends Maybe[E, V]

但是,当我使用 for 理解来组合 Maybe 和 Future 时,Scala 编译器会在外部生成器的行中向我显示错误消息 missing parameter type:

def retrieveStreet(id: String): Future[Maybe[String, String]] = ...

val outerMaybe: Maybe[String, String] = ...

val result = for {
      id ← outerMaybe // error message "missing parameter type" here!
      street ← retrieveStreet(id)
    } yield street

但是当我不使用for 而是显式调用flatMap和map方法时,它可以工作:

val result2 =
  outerMaybe.flatMap( id => retrieveStreet(id) )
            .map( street => street )

(当我尝试将 Maybe 与另一个 Maybe 组合到 for comprehension 中时,我也会收到此错误消息。)

所以问题是:

  1. 这两个替代方案的行为不应该完全相同吗?为什么编译器在显式调用flatMap 时找出正确的flatMap 调用方法?

  2. 由于编译器显然被两个flatMap 实现混淆了,有没有办法告诉它(通过任何地方的类型规范)应该在 for 理解中调用哪个?

    李>

我在 Eclipse 中使用 Scala 2.11.8。

【问题讨论】:

  • 我很惊讶,你的 flatMap 重载甚至可以编译(很可能,scalac 只是混淆了,并显示一个错误的错误。尝试删除 for 理解并查看它是否编译)。
  • 我认为它不应该编译的原因是flatMap 的两个版本在类型擦除后具有相同的签名。这是不允许的。我认为在这里做你想做的事是不可能的,但很高兴知道是否有人能证明我错了。
  • @Dima 如果问题出在同一个签名上,为什么替代表示法 (flatMap/map) 有效?

标签: scala functional-programming


【解决方案1】:

我不能给你一个全面的答案,但是通过scalac -Xprint:parser 运行它,我可以告诉你,这两种替代品实际上脱糖略有不同,很有可能这是你问题的根源。

val result1 = outerMaybe
  .flatMap(((id) => retrieveStreet(id)
  .map(((street) => street))));

val result2 = outerMaybe
  .flatMap(((id) => retrieveStreet(id)))
  .map(((street) => street))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-13
    • 1970-01-01
    • 2015-03-19
    • 2022-01-21
    • 2011-12-29
    • 2017-10-30
    • 2020-08-04
    • 1970-01-01
    相关资源
    最近更新 更多