2012-01-05 96 views
11

請考慮case class Foo[A, B <: List[A]](l: B) { ... }或類似的東西。特別是,A以及B需要在Foo正文的某處可用。基本類型推理

編譯器是否可以自動推斷出A?例如,Foo(List(1,2,3))因類型檢查程序推斷ANothing而失敗。也許有一種方法可以通過使用類型成員來解決這個問題?

我有說我忽視的東西embarassingly簡單這裏一定感覺;)

編輯:我剛剛發現使用其他類型的參數X作品不錯,但我的ATM不明白這是爲什麼所以:

scala> case class Bar[A, B[X] <: List[X]](l: B[A]) 
defined class Bar 

scala> Bar(List(1,2,3)) 
res11: Bar[Int,List] = Bar(List(1, 2, 3)) 

有人可以請給我解釋一下嗎? 這是一個統一的問題嗎?

編輯2:使用[A, B[X] <: List[X]](l: B[A])可能會對某些層次結構產生不希望的影響(儘管這並不是什麼大問題)。更有趣的是,我剛跨blog post跌跌撞撞由Josh Suereth隱含表明[A, B <: List[A]](l: B with List[A])作品一樣好......無需implicits等

+0

您可以將類定義爲case case Foo [A](l:List [A])'嗎?這可能更清楚。 – leedm777 2012-01-05 21:25:32

+0

我明白你的意思,但這不適用於我的情況。 A型和B型都必須彼此分開。 – fotNelton 2012-01-05 21:54:33

回答

2

根據Alexey Romanov,原則上可以自動推斷類型,但目前並不是。

因此,至少現在,應該推斷的所有類型參數都必須在參數中顯式顯示。

5

它並沒有真正回答「爲什麼」的一部分,很抱歉,但在這裏你可以玩更多的技巧。首先,由於你不使用你的榜樣的X,你可以寫:

case class Bar[A,B[_] <: Seq[_]](l : B[A]) 

然後:

scala> Bar(List(1,2,3)) 
resN: Bar[Int,List] = Bar(List(1, 2, 3)) 

(我使用的協變的Seq代替List顯示它的工作原理另外,請注意,這不等同於使用額外的X,請參閱註釋。)不幸的是,每次要使用序列類型時,都需要編寫B[A],即手動實例化它。解決方法之一就是,而不是寫:

case class Bar[A,B](l : B)(implicit ev : B <:< Seq[A]) 

在行動:

scala> Bar(List(1,2,3)) 
resN: Bar[Int,List[Int]] = Bar(List(1, 2, 3)) 

...,你會得到實例化,就像你一直都知道他們應該類型參數AB

+1

這是個不好的建議。永遠不要寫「B [_] <:Seq [_]」,這並不意味着你的想法。他確實使用X來將B的類型參數鏈接到Seq's。 「B [_] <:Seq [_]」中的下劃線不是同一類型。 – extempore 2012-01-06 06:42:26

+0

除了'_'問題,我在一篇有趣的文章中發現了這個問題,因爲我從來不會猜測隱式視圖可以提供幫助。那麼編譯器在哪裏找到'B'的隱式視圖作爲'Seq [A]'?當我在控制檯嘗試時發現Predef有一些功能,但它也可以與自定義類一起工作(顯然,本節中沒有可能具有格式良好的示例,對不起)。有沒有一種方法可以跟蹤編譯器引入的含義? – fotNelton 2012-01-06 08:22:23

+0

啊,好的,看看Predef的來源揭示了<:<,= = =和<%<的「基礎設施」。 – fotNelton 2012-01-06 08:53:18

5

編譯器不可能自動推斷A.但如果可能的話,它必須說A應該是Int的超類型,所以Int或Any,不僅僅是Int !.因爲列表[Int] <:List [Any]。所以,如果編譯器會推斷Int爲A,那麼它會受到太多的限制。

換句話說:

  1. case class Foo[A, B <: List[A]](l: B) { ... } ,然後調用它Foo(List(1,2,3))你說A必須是其中認爲它的名單應該是int列表的超類型。 因此有效的A必須是Int的超類型,因爲List是協變的。

  2. case class Bar[A, B[X] <: List[X]](l: B[A]) ,然後調用它Foo(List(1,2,3))你說A必須是int

情況(2)沒有留下空間讓A成爲除Int以外的其他東西。然而,情況(1)爲A留下空間而不是Int,例如,它可能是任何。 你可以看到這個,因爲你可以通過Foo[Any, List[Int]](List(1,2,3))來調用它。 (2)情況下你將無法做到這一點。

所以這兩種情況並不相同。

+0

謝謝,'Foo [Any,List [Int]]'是一個很好的例子。 OTOH我想知道爲什麼,根據你的推理,仍然不可能說'Foo [A,B>:List [A] <:List [A]]「,因爲從邏輯的角度看,刪除模棱兩可(或者不是,我想知道?)。然而,在這兩種情況下,即後者以及我原來的帖子中提到的那種情況下,編譯器都會將「A」推斷爲「Nothing」,並不斷抱怨(從我的有限觀點來理解),這就是爲什麼我在想除了協方差問題外,這也可以用統一來解釋? – fotNelton 2012-01-08 21:57:04

+0

是的,我遵循你的推理。我想這就是scala類型推理不完整的含義。 – 2012-01-08 23:27:02

+0

Daniel Spiewack提供了關於(不僅是Scala的)不完整類型推斷的非常好的[talk](http://screencasts.chariotsolutions.com/webpage/2011/10)。介紹相當長,但整個談話非常值得。 – fotNelton 2012-01-09 07:39:13