2013-02-06 105 views
11

我有一些Scala从类型投影中推断出正确类型的麻烦。如何从投影类型推断正确的类型参数?

考虑以下几点:

trait Foo { 
    type X 
} 

trait Bar extends Foo { 
    type X = String 
} 

def baz[F <: Foo](x: F#X): Unit = ??? 

那么下面编译罚款:

val x: Foo#X = ???  
baz(x) 

但以下不会编译:

val x: Bar#X = ???  
baz(x) 

斯卡拉认为“基础String类型“为x,但已丢失的信息是xBar#X

baz[Bar](x) 

有没有一种方法,使斯卡拉推断baz正确类型的参数:如果我注释类型,它工作正常?
如果不是,那么通用答案是什么使它不可能?

+2

不是一个答案,但值得注意的是,如果您使用类型指示符而不是类型投影来键入'x',它将起作用 - 例如, '对象BAR扩展Bar; val x:BAR.X =“a”;巴兹(X)'。 –

+3

另外值得注意的是,你可以说服编译器,你真的想把'x'作为或多或少的像'Bar#X'那样令人难以置信的'val x:bX forSome {val b:Bar} =“ a“:bX forSome {val b:Bar}'。 –

+0

似乎很奇怪的用例。你为什么想这样做? –

回答

2

程序编译通过上下文添加此隐式转换:

implicit def f(x: Bar#X): Foo#X = x 

由于这种隐式转换为任何F <: Foo正确的,我不知道为什么编译器不这样做本身。

1

你还可以:

trait Foo { 
    type X 
} 
trait Bar extends Foo { 
    type X = String 
} 
class BarImpl extends Bar{ 
    def getX:X="hi" 
} 
def baz[F <: Foo, T <: F#X](clz:F, x: T): Unit = { println("baz worked!")} 
val bi = new BarImpl 
val x: Bar#X = bi.getX 
baz(bi,x) 

但:

def baz2[F <: Foo, T <: F#X](x: T): Unit = { println("baz2 failed!")} 
baz2(x) 

失败:

test.scala:22: error: inferred type arguments [Nothing,java.lang.String] do not conform to method baz2's type parameter bounds [F <: this.Foo,T <: F#X] 
baz2(x) 
^ 
one error found 

我认为基本上,F <:富告诉那个F必须是编译器Foo的一个子类型,但是当它得到一个X时它不知道你的类是什么类特定的 X来自。你的X只是一个字符串,并不保留指向Bar的信息。

需要注意的是:

def baz3[F<: Foo](x : F#X) = {println("baz3 worked!")} 
baz3[Bar]("hi") 

也有效。你定义了一个val x的事实:Bar#X = ???只是意味着?被限制在Bar#X可能发生在编译时的任何地方......编译器知道Bar#X是String,所以x的类型只是一个与其他String没有区别的字符串。

+0

是的,编译器只能看到'String'并且失去'Foo'上下文,尽管用'Bar#X'而不是'String'注解类型。到目前为止,我唯一的解决方案是总是抽象出'Foo'并在最后提供'Bar'。但是这对我来说确实是一个问题,因为它限制了我可以做的'baz'...(在我的情况下,'baz'也是一个隐含的) – betehess

+0

没错。你可以这样想:类型X是Foo的成员,类似于Java中的其他静态和成员变量。试图在没有实例的情况下访问它就像试图访问没有实例的成员变量。 – Brian

+0

In: 'trait HasType {type X} class Foo(val t:HasType){type A = t.X}'。你可以有许多具有不同'A'的'Foo',每个''都依赖于特定的传递't'('HasType'的实例)。该类型是动态的,因此必须以某种方式指定。在这里的例子中,Foo#A并没有告诉你或者编译器更多关于'A'的信息,而Person.age会告诉你一个人实例的年龄。一个实例't'提供了类型信息。 – Brian