Scala &引用;缺少参数类型“;在重载flatMap时用于理解

Scala &引用;缺少参数类型“;在重载flatMap时用于理解,scala,functional-programming,Scala,Functional Programming,我编写了自己的类似monad的类,名为Maybe,其中包含一个值或一个错误对象。我希望这个类的对象与Future相结合,这样我就可以将可能的[Future[T],E]]变成可能的[T,E]。因此,我实现了两种flatMap方法: import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future sealed abstract class Maybe[+E, +V] { def

我编写了自己的类似monad的类,名为
Maybe
,其中包含一个值或一个错误对象。我希望这个类的对象与
Future
相结合,这样我就可以将
可能的[Future[T],E]]
变成
可能的[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 CONTRUMENT组合一个
Maybe
和一个
Future
,其中包含另一个
Maybe
时,Scala编译器会在外部生成器的行中给我错误消息
缺少参数类型

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
但是当我显式调用
flatMap
map
方法而不是使用
for
时,它会起作用:

val result2 =
  outerMaybe.flatMap( id => retrieveStreet(id) )
            .map( street => street )
(当我试图将一个
Maybe
与另一个
Maybe
组合在一起以便于理解时,也会收到此错误消息。)

因此,问题是:

  • 这两个备选方案的行为不应该完全相同吗?当显式调用
    flatMap
    时,编译器为什么要找出要调用的正确
    flatMap
    方法

  • 由于编译器显然被这两个
    flatMap
    实现搞糊涂了,有没有办法告诉它(通过任何地方的类型规范)应该调用哪一个来理解


  • 我在Eclipse中使用Scala 2.11.8。

    我不能给您一个全面的答案,但是通过运行
    scalac-Xprint:parser
    ,我可以告诉您,这两个替代方案实际上在desugar方面略有不同,这很可能是您问题的根源

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

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