Scala 上界函数中类型推断的奇异行为

Scala 上界函数中类型推断的奇异行为,scala,type-inference,lower-bound,Scala,Type Inference,Lower Bound,在实现中更改上限时遇到这种奇怪的行为,但忘记在接口中更改它。我认为最后一条语句不应该编译,但它编译了,并返回了意外的结果 trait SuperBase trait Base extends SuperBase class SuperBaseImpl extends SuperBase trait Service { def doWork[T <: Base : Manifest](body: T => Unit): String def print[T <: Ba

在实现中更改上限时遇到这种奇怪的行为,但忘记在接口中更改它。我认为最后一条语句不应该编译,但它编译了,并返回了意外的结果

trait SuperBase
trait Base extends SuperBase

class SuperBaseImpl extends SuperBase

trait Service {
  def doWork[T <: Base : Manifest](body: T => Unit): String
  def print[T <: Base : Manifest]: String
}

object ServiceImpl extends Service {
  override def doWork[T <: SuperBase : Manifest](body: T => Unit): String =
    print[T]
  def print[T <: SuperBase : Manifest]: String =
    manifest[T].runtimeClass.toString
}

val s: Service = ServiceImpl

// does not compile as expected
// s.print[SuperBaseImpl]

// returns "interface Base"
s.doWork { x: SuperBaseImpl => () }

这就解释了为什么我们得到了“接口基础”。但我仍然不太明白在这种情况下,类型推断是如何工作的,以及为什么工作的。

它看起来很奇怪,但感觉很正常。请注意,您也可以

s.doWork { x: Any => () }
我只是认为类型参数
T
在某种程度上是“无人居住的”。该方法除了它的上限
Base
之外,不能知道关于
T
的任何信息,因此您将获得
Base
的清单。但同样地,你不能做太多,因为这不能构造一个类型为
T
…的值,所以一切都保持完好

尝试将签名更改为

def doWork[T <: Base : Manifest](x: T)(body: T => Unit): String

使用
-Xprint:typer
,您将看到编译器对
T
的推断:

s.doWork[Base with SuperBaseImpl]
边界试图表达什么?函数在参数中是共变的,因此表示
body
必须接受足够窄类型的特定参数。通常,您要求函数必须处理宽类型

也许你想要一个下限

scala> trait SuperBase
defined trait SuperBase

scala> trait Base extends SuperBase
defined trait Base

scala> class SuperBaseImpl extends SuperBase
defined class SuperBaseImpl

scala> trait Service { def f[A >: Base : Manifest](g: A => Unit): String }
defined trait Service

scala> object Impl extends Service { def f[A >: Base : Manifest](g: A => Unit) = manifest[A].runtimeClass.toString }
defined object Impl

scala> (Impl: Service).f { x: Base => () }
res0: String = interface Base

scala> (Impl: Service).f { x: SuperBase => () }
res1: String = interface SuperBase

scala> (Impl: Service).f { x: SuperBaseImpl => () }
<console>:17: error: inferred type arguments [SuperBaseImpl] do not conform to method f's type parameter bounds [A >: Base]
       (Impl: Service).f { x: SuperBaseImpl => () }
                       ^
<console>:17: error: type mismatch;
 found   : SuperBaseImpl => Unit
 required: A => Unit
       (Impl: Service).f { x: SuperBaseImpl => () }
                                            ^
<console>:17: error: No Manifest available for A.
       (Impl: Service).f { x: SuperBaseImpl => () }
                         ^

scala> object Impl extends Service { def f[A >: SuperBase : Manifest](g: A => Unit) = manifest[A].runtimeClass.toString }
<console>:14: error: overriding method f in trait Service of type [A >: Base](g: A => Unit)(implicit evidence$1: Manifest[A])String;
 method f has incompatible type
       object Impl extends Service { def f[A >: SuperBase : Manifest](g: A => Unit) = manifest[A].runtimeClass.toString }
                                         ^
scala>trait SuperBase
定义特征超基
scala>trait Base扩展了SuperBase
定义性状库
scala>class SuperBaseImpl扩展了SuperBase
定义类SuperBaseImpl
scala>trait服务{def[A>:Base:Manifest](g:A=>Unit):String}
定义特征服务
scala>objectimpl扩展服务{def[A>:Base:Manifest](g:A=>Unit)=Manifest[A].runtimeClass.toString}
定义对象Impl
scala>(Impl:Service).f{x:Base=>()}
res0:String=接口基
scala>(Impl:Service).f{x:SuperBase=>()}
res1:String=接口超基址
scala>(Impl:Service).f{x:SuperBaseImpl=>()}
:17:错误:推断的类型参数[SuperBaseImpl]不符合方法f的类型参数界限[A>:Base]
(Impl:Service).f{x:SuperBaseImpl=>()}
^
:17:错误:类型不匹配;
找到:SuperBaseImpl=>Unit
必需:A=>单位
(Impl:Service).f{x:SuperBaseImpl=>()}
^
:17:错误:没有可用于的清单。
(Impl:Service).f{x:SuperBaseImpl=>()}
^
scala>objectimpl扩展服务{def[A>:SuperBase:Manifest](g:A=>Unit)=Manifest[A]。runtimeClass.toString}
:14:错误:重写[A>:Base](g:A=>Unit)(隐式证据$1:Manifest[A])字符串类型的trait服务中的方法f;
方法f的类型不兼容
对象Impl扩展服务{def[A>:SuperBase:Manifest](g:A=>Unit)=Manifest[A]。runtimeClass.toString}
^

请注意,您的代码所说的是:

方法ServeImp.doWork必须接受一个参数,该参数是“一个必须接受某个类T的函数,该类T是Base和Superbase的子类”

SuperBaseImpl不是Base的子类,但这不是一个错误,因为可能存在一个类X,该类“用Base扩展SuperBaseImpl”,可以满足该要求

当发生类型推断时,T被解析为“foo.Base with foo.SuperBaseImpl”,它满足上述所有要求。runtimeClass是接口基础,因为在运行时无法在JVM中描述该类型,但如果执行manifest.toString,您将看到正确的类型

没有真正的方法来证明你的例子,但是考虑下面的:

trait SuperBase
trait Base extends SuperBase

class SuperBaseImpl(val a: String) extends SuperBase

trait Service {
  def doWork[T <: Base : Manifest](body: T => String): (T) => String
}

object ServiceImpl extends Service {
  override def doWork[T <: SuperBase : Manifest](body: T => String): (T) => String =
    x => "Manifest is '%s', body returned '%s'".format(manifest[T].toString(), body(x))
}

val s: Service = ServiceImpl

val f = s.doWork { x: SuperBaseImpl => x.a }
// f: Base with SuperBaseImpl => String = <function1>

f(new SuperBaseImpl("foo") with Base)
// res0: String = Manifest is 'Base with SuperBaseImpl', body returned 'foo'

f(new SuperBaseImpl("foo"))
// compile error 
trait超基
特征基扩展超基
类SuperBaseImpl(val a:String)扩展了SuperBase
特色服务{
def doWork[T字符串):(T)=>字符串
}
对象ServiceImpl扩展服务{
覆盖def doWork[T字符串):(T)=>字符串=
x=>“清单为'%s',正文返回'%s'。格式(清单[T].toString(),正文(x))
}
val s:Service=ServiceImpl
val f=s.doWork{x:SuperBaseImpl=>x.a}
//f:Base与SuperBaseImpl=>String=
f(带基的新SuperBaseImpl(“foo”)
//res0:String=Manifest为'Base with SuperBaseImpl',正文返回'foo'
f(新的SuperBaseImpl(“foo”))
//编译错误
在这里,我让doWork返回另一个接受T的函数,您可以看到它解析为什么,您可以实际调用它,如果您传递与所有类型上的约束匹配的内容,它将正常工作

增加:

还要注意的是,类层次结构根本不需要显示该行为,它们可以是完全不相关的

trait A
trait B

def m[T <: A : Manifest](body: T => Unit) = manifest[T].toString()

m((x: B) => Unit)
//res0: String = A with B
trait A
性状B
def m[T单位)=清单[T].toString()
m((x:B)=>单位)
//res0:String=A和B

谢谢,但我认为直接定义
T
更容易:
s.doWork[SuperBaseImpl]{x=>()}
。感谢使用
-Xprint:typer
的技巧,这让我更清楚了。但我仍然不明白它是如何以及为什么以这种方式工作的。这是一个bug吗?关于边界,在调用参数函数之前/之后,上限被用来调用Base/SuperBase中定义的一些方法。然后,它在Impl中被错误地更改了.
trait SuperBase
trait Base extends SuperBase

class SuperBaseImpl(val a: String) extends SuperBase

trait Service {
  def doWork[T <: Base : Manifest](body: T => String): (T) => String
}

object ServiceImpl extends Service {
  override def doWork[T <: SuperBase : Manifest](body: T => String): (T) => String =
    x => "Manifest is '%s', body returned '%s'".format(manifest[T].toString(), body(x))
}

val s: Service = ServiceImpl

val f = s.doWork { x: SuperBaseImpl => x.a }
// f: Base with SuperBaseImpl => String = <function1>

f(new SuperBaseImpl("foo") with Base)
// res0: String = Manifest is 'Base with SuperBaseImpl', body returned 'foo'

f(new SuperBaseImpl("foo"))
// compile error 
trait A
trait B

def m[T <: A : Manifest](body: T => Unit) = manifest[T].toString()

m((x: B) => Unit)
//res0: String = A with B