为什么编译器无法弄清楚(_>:T)=> (_<:V [_&lt ;: U])<:T => V [+]为V [+]?

时间:2016-12-18 22:48:16

标签: scala existential-type

所以我玩了一会儿,试图写出关于存在感和方差的东西,我偶然发现了这段有趣的代码。

final case class Box[+T](val value: T) {
  def >>=[U](f: T => Box[U]) = f(value)
  def flatMap[U](f: (_ >: T) => (_ <: Box[_ <: U])): Box[U] = this >>= f
}

无法编译:

Variance.scala:3: no type parameters for method >>=: (f: T => Box[U])Box[U] exist so that it can be applied to arguments (_$1 => _$2)
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : _$1 => _$2 where type _$2 <: Box[_ <: U], type _$1 >: T
 required: T => Box[?U]
  def flatMap[U](f: (_ >: T) => (_ <: Box[_ <: U])): Box[U] = this >>= f
                                                                   ^
Variance.scala:3: type mismatch;
 found   : _$1 => _$2 where type _$2 <: Box[_ <: U], type _$1 >: T
 required: T => Box[U]
  def flatMap[U](f: (_ >: T) => (_ <: Box[_ <: U])): Box[U] = this >>= f
                                                                       ^

我觉得很奇怪,因为(_ >: T) => (_ <: Box[_ <: U])不是T => Box[U]的子类型?由于Function1在第一个类型参数中是逆变的,因此这是T => (_ <: Box[_ <: U])的子类型。由于Function1在结果类型中是协变的,因此这是T => Box[_ <: U]的子类型,并且由于Box在其参数中是协变的,因此整个事物不是{{1}的子类型}?

奇怪的是,将代码更改为

T => Box[U]

使用类型归属来“提示”// This change is not required ;) type Box[T] = `What A Category Theorist Calls "The Identity Monad" And What Everyone Else Calls "A Box"`[T] final case class `What A Category Theorist Calls "The Identity Monad" And What Everyone Else Calls "A Box"`[+T](val value: T) { def >>=[U](f: T => Box[U]) = f(value) def flatMap[U](f: (_ >: T) => (_ <: Box[_ <: U])): Box[U] = this >>= (f: T => Box[U]) } 使其编译的编译器。既然没有隐式转换或变量声明,这不应该没有区别吗?

我发现编译的另一种方法是编写

f: T => Box[U]

这让我相信,编译器很难得到def flatMap[U](f: (_ >: T) => (_ <: Box[_ <: U])): Box[U] = this.>>=[U](f) 以及更多因为它无法推断出(_ >: T) => (_ <: Box[_ <: U]) <: T => Box[U]的类型参数时问题更少,这似乎是什么错误信息暗指。

(使用Scala 2.12.1(使用sbt,如果改变了什么))

1 个答案:

答案 0 :(得分:0)

final case class Box[+T](val value: T) {
  def >>=[U](f: T => Box[U]) = f(value)
  def flatMap[U](f: (_ >: T) => (_ <: Box[_ <: U])): Box[U] = this >>= f
}

flatMap retrun类型为Box[U],但要使用this >>= f

因此>>=会自动更改为f类型(_ >: T) => (_ <: Box[_ <: U])

因此Box[(_ >: T) => (_ <: Box[_ <: U])]Box[U]不匹配。

我认为你可以这样改变:
def flatMap[U](f: (_ >: T) => (_ <: Box[_ <: U])): Box[U] = this >>=[U] f