2012-07-23 70 views
1

由於具有表示類型的特徵是自引用的,因此聲明變量包含該特徵的實例有點困難。在這個例子中,我簡單地聲明一個變量保存特性的一個實例,聲明功能需要和特點的回報和實例,並調用與變量函數:Scala:使用表示類型

trait Foo[+A <: Foo[A]] 
case class Bar() extends Foo[Bar] 
case class Grill() extends Foo[Grill] 

// Store a generic instance of Foo 
val b: Foo[_] = if(true) { 
    Bar() 
} else { 
    Grill() 
} 

// Declare a function that take any Foo and returns a Foo of the same type 
// that "in" has in the calling context 
def echoFoo[A <: Foo[A]](in: A): A = in 

// Call said function 
val echo = echoFoo(b) 

它失敗,出現錯誤:現在

inferred type arguments [this.Foo[_$1]] do not conform to method 
echoFoo's type parameter bounds [A <: this.Foo[A]] 
val echo = echoFoo(b) 
     ^

,這是有道理的,因爲[_]就像Any(的方式我不完全理解)。它可能需要的是類似Foo[Foo[_]]的東西,以便類型參數符合A <: Foo[A]的範圍。但是現在有一個內部的Foo,它具有不一致的類型參數,表明解決方案類似於Foo[Foo[Foo[Foo[...,這顯然不正確。

所以我的問題可能被提取到:什麼是「這個變量持有任何合法Foo」Scala語法?

+0

正如我在回答中所說的,這些問題是由於設計不佳造成的。如果你向我們解釋你想做什麼,我們可以討論這個最佳設計 – Edmondo1984 2012-07-24 07:01:45

+0

@ Edmondo1984你不是第一個建議我的基本設計可能是錯誤的人。但是,當然,如果不知道我想用Scala來完成什麼,你不能提出修復建議。我將發佈一個後續問題,這將更接近我的實際問題。 – drhagen 2012-07-24 12:52:46

+0

在這裏發佈引用,所以我們可以嘗試幫助 – Edmondo1984 2012-07-24 13:08:40

回答

2

像這樣的自引用類型參數有點問題,因爲它們不健全。例如,它可以定義如下所示的類型:

case class BeerGarden extends Foo[Grill] 

正如你所看到的,在A <:富[A]結合不夠緊密。我喜歡在這樣的情況下是用蛋糕圖案和抽象類型成員:

trait FooModule { 
    type Foo <: FooLike 

    def apply(): Foo 

    trait FooLike { 
    def echo: Foo 
    } 
} 

現在,您可以遞歸,安全地使用美孚類型:

object Foos { 
    def echo(foo: FooModule#Foo) = foo.echo 
} 

顯然,這是不一個理想的解決方案,您可能想要解決這些類型的所有問題,但重要的觀察結果是FooLike是一個可擴展的特性,因此您可以繼續細化FooLike以添加所需的成員,而不會違反界限類型成員的目的是強制執行。我發現,在每一個我想代表的類型集都沒有關閉的現實世界的情況下,這是關於可以做的最好的事情。重要的是,FooModule抽象了類型實例構造函數,同時強制執行「自我類型」。如果不抽象出另一個,你就不能抽象出一個。

對這樣的事情(以及一些與遞歸類型我自己的早期鬥爭的記錄的)一些額外的信息,請訪問:

https://issues.scala-lang.org/browse/SI-2385

+0

'Foos.echo'的返回類型是什麼? – drhagen 2012-07-23 20:47:33

+0

這是FooModule#Foo – 2012-07-23 20:53:17

+0

如果在'FooModule#Foo'的具體實例上調用'Foos.echo',比如說'BarModule#Foo',它會返回一個具體的實例,還是會被downcast爲'FooModule#Foo '? – drhagen 2012-07-23 21:02:45

0

雖然我同意的傳播仿製藥存在的問題,當你遇到這個問題時,你應該在你的屏幕上看到一個很大的警告,因爲它通常是一個糟糕的設計的標誌。這些是關於這個主題的一般性建議。

  • 如果您使用泛型,那麼type參數是有原因的。它允許您通過傳入或接收類型A的參數,並以類型安全的方式與Foo [A]進行交互,並允許您對A進行約束。如果丟失類型信息,則會失去類型安全性,如果您不再需要通用類,那麼就沒有必要編寫泛型類:您可以將所有簽名更改爲任何並進行模式匹配。

  • 在大多數情況下,遞歸類型可以通過實施類似的藏品CanBuildFrom方法,使用了「類型類」

  • 最後是可以避免的,類型投影(FooModule#富)的小程序和你可能想看看路徑相關的類型。但是,這些也沒有什麼用處。