2016-11-09 31 views
8

所以這是this Java question Scala的一個相當直接端口在斯卡拉避免重複泛型參數

我們有一堆採取通用的參數特性如下:

trait Ident { } 

trait Container[I <: Ident] { 
    def foo(id: I): String 
} 

trait Entity[C <: Container[I], I <: Ident] { 
    def container: C 
    def foo(id: I) = container.foo(id) 
} 

這工作,但它是一個小因爲在定義實體的子類時,我們必須提供Ident的類型和Container的類型。而事實上,就在容器的類型將是自身足夠的類型信息:

class MyIdent extends Ident { } 
class MyContainer extends Container[MyIdent] { } 
class MyEntity extends Entity[MyContainer,MyIdent] { } 
//          ^^^^^^^ shouldn't really be necessary 

使用一個存在的類型避免了需要實體採取兩個參數......當然你不能引用它稍後的。

trait Entity[C <: Container[I] forSome { type I <: Ident }] { 
    def container: C 
    def foo(id: I) = container.foo(id) 
//   ^^^ complains it has no idea what 'I' is here 
} 

同樣將使用成員類型也不起作用的東西...

trait Ident { } 

trait Container { 
    type I <: Ident 
    def foo(id: I): String 
} 

trait Entity { 
    type C <: Container 
    def container: C 
    def foo(id: C#I) = container.foo(id) 
//         ^^ type mismatch 
} 

因此,沒有人知道,如果有一個優雅的解決方案,以在斯卡拉這個問題呢?

+0

我不認爲答案會與Java版本大不相同。沒有一種真正的方法可以省略類型參數,而不會丟失隨之而來的類型信息。 –

+0

是否可以將'container'設爲'val'? – sepp2k

+0

@MichaelZajac所以在「MyEntity」的定義中,提供給Entity的第二個參數是多餘的:除了「MyIdent」之外,根本沒有其他可能的類型可用,實際上其他任何類型都會導致編譯錯誤。當然,是否可以避免Scala中的冗餘是另一個問題:-) –

回答

4

更新給出this answer我不知道這是否應該被認爲是錯誤或不

你已經打SI-4377;如果你提供明確的type ascriptions你會得到我猜的錯誤只是暴露了類型預測是實現使用existentials:

trait Ident { } 

trait Container { 
    type I <: Ident 
    def foo(id: I): String 
} 

trait Entity { 

    type C <: Container 
    def container: C 
    def foo(id: C#I): String = (container: C).foo(id: C#I) 
    // you will get something like: type mismatch; 
    // [error] found : Entity.this.C#I 
    // [error] required: _3.I where val _3: Entity.this.C 
    // as I said above, see https://issues.scala-lang.org/browse/SI-4377 
} 

這不是一個輕描淡寫地說,這(錯誤?)讓與泛型編程類型成員是一場噩夢。

雖然黑客,其中包括在鑄造到手工製作的自我指涉的類型別名:

case object Container { 

    type is[C <: Container] = C with Container { 

    type I = C#I 
    // same for all other type members, if any 
    } 

    def is[C <: Container](c: C): is[C] = c.asInstanceOf[is[C]] 
} 

現在使用它,Entity編譯:

trait Entity { 

    type C <: Container 
    def container: C 
    def foo(id: C#I): String = Container.is(container).foo(id) 
    // compiles! 
} 

這當然是危險的,根據經驗,只有在C及其所有類型成員都綁定到非抽象類型時纔是安全的它將被使用的地步;請注意,這並不總是如此,因爲Scala讓你留下「未定義」類型成員:

case object funnyContainer extends Container { 

    // I'm forced to implement `foo`, but *not* the `C` type member 
    def foo(id: I): String = "hi scalac!" 
} 
+1

謝謝,這個作品。很高興知道我有正確的想法,但只是遇到了一個錯誤:-) –