joe*_*cii 15 types scala typechecking
假设我有一个空的标记特征命名Marker,一些函数的类型参数绑定Marker:
trait Marker
object Marker {
def works[M <: Marker](m:M):M = m
def doesntWork[M <: Marker](f:M => String):String = "doesn't matter"
}
Run Code Online (Sandbox Code Playgroud)
第一个功能正如我所料.也就是说,如果传递的参数不是a Marker,则代码不会编译:
scala> works("a string")
<console>:14: error: inferred type arguments [String] do not conform to method works's type parameter bounds [M <: com.joescii.Marker]
works("a string")
^
<console>:14: error: type mismatch;
found : String("a string")
required: M
works("a string")
^
Run Code Online (Sandbox Code Playgroud)
但是,我能够将参数传递给不符合的第二个函数Marker.具体来说,我可以传递一个类型的函数,String => String代码愉快地编译并运行:
scala> doesntWork( (str:String) => "a string" )
res1: String = doesn't matter
Run Code Online (Sandbox Code Playgroud)
我希望这个调用doesntWork无法编译.任何人都可以向我解释为什么它编译以及如何更改函数签名以防止类型在这种情况下检查?
完全披露:上述设计的例子是lift-ng这个突出问题的简化版本.
代码由于逆转而编译.您可以通过显式提供推断类型参数来查看:
doesntWork[Nothing]((str: String) => "a string")
Run Code Online (Sandbox Code Playgroud)
这是一个普遍的问题.有各种各样的技术可以解决这个问题,但它们通常归结为限制T为某种类型的实例.
M => String实际上是一个Function1[M, String]. 如果你看一下定义:
trait Function1[-T1, +R]
Run Code Online (Sandbox Code Playgroud)
因此M变得逆变,这意味着对于M1 >: M2, Function1[M1, String] <: Function1[M2, String],我们可以说M1 = Any那么Function1[Any, String] <: Function1[Marker, String]。
并且doesntWork-的输入f也是逆变的,这意味着您可以传递小于 的东西M => String,并且正如我刚刚所示,Any => String小于Marker => String,因此它完全可以正常传递。
你也可以String => String因为你的而通过[M <: Marker],这最终导致编译器解释M为Nothing,所以甚至String => String变得大于M => String。
要解决你的问题,只需引入包装器,这将使你的类型不变:
scala> case class F[M](f: M => String)
defined class F
scala> def doesntWork[M <: Marker](f:F[M]):String = "doesn't matter"
doesntWork: [M <: Marker](f: F[M])String
scala> doesntWork(F((str: String) => "a string"))
<console>:18: error: inferred type arguments [String] do not conform to method doesntWork's type parameter bounds [M <: Marker]
doesntWork(F((str: String) => "a string"))
^
<console>:18: error: type mismatch;
found : F[String]
required: F[M]
doesntWork(F((str: String) => "a string"))
^
scala> doesntWork(F((str: Any) => "a string"))
<console>:18: error: inferred type arguments [Any] do not conform to method doesntWork's type parameter bounds [M <: Marker]
doesntWork(F((str: Any) => "a string"))
^
<console>:18: error: type mismatch;
found : F[Any]
required: F[M]
Note: Any >: M, but class F is invariant in type M.
You may wish to define M as -M instead. (SLS 4.5)
doesntWork(F((str: Any) => "a string"))
scala> doesntWork(F((str: Marker) => "a string"))
res21: String = doesn't matter
scala> trait Marker2 extends Marker
defined trait Marker2
scala> doesntWork(F((str: Marker) => "a string"))
res22: String = doesn't matter
scala> doesntWork(F((str: Marker2) => "a string"))
res23: String = doesn't matter
Run Code Online (Sandbox Code Playgroud)
推荐这样的隐式转换通常是不好的,但在这里似乎很好(如果你不会过度使用F):
scala> implicit def wrap[M](f: M => String) = F(f)
warning: there was one feature warning; re-run with -feature for details
wrap: [M](f: M => String)F[M]
scala> doesntWork((str: Marker) => "a string")
res27: String = doesn't matter
scala> doesntWork((str: String) => "a string")
<console>:21: error: inferred type arguments [String] do not conform to method doesntWork's type parameter bounds [M <: Marker]
doesntWork((str: String) => "a string")
^
<console>:21: error: type mismatch;
found : F[String]
required: F[M]
doesntWork((str: String) => "a string")
^
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
757 次 |
| 最近记录: |