2015-07-22 90 views
0

我想在運行時恢復Scala中的依賴類型。我基本上想要歸檔一個類型保存映射,其中每個鍵都有一個關聯的類型,但所有存儲的鍵值對的類型信息對Map的用戶不可見(與真棒Shapeless Map不同)。在運行時恢復依賴類型

class Key[V] { 
    type Value = V 
    def ->(value: V) = Pair(this, value) 
} 
trait Pair { 
    val key: Key[_] 
    val value: key.Value 
} 
trait Map { 
    val pairs: Seq[Pair] 
    def get[V](key: Key[V]): Option[V] = 
    pairs.find(pair => pair.key eq key).map(_.value).asInstanceOf[Option[V]] 
    //      ^       ^
    // the runtime prove that pair.key.Value equals V  | 
    //               | 
    //      'Convince' the compile that I know what I do 
} 

用法:

val text = new Key[String] 
val count = new Key[Int] 
val map: Map = new Map { val pairs = text -> "Hello World!" :: Nil } 

map.get(text) // Some(Hello World!), Type: Option[String] 
map.get(count) // None, Type: Option[Int] 

是否可以寫一個get方法,無需使用顯式與asInstanceOf或隱式鑄造用火柴有未經檢查的分支?

我試圖寫一對unapply對,但遇到同樣的問題。

請注意,我省略了Pair-companion對象的定義。這裏的一個運行示例在Gist

回答

1

記住JVM運行時擦除泛型。因此,任何依賴於泛型的依賴包括依賴類型都只能在編譯時發生 - 即在調用者中,因爲任何給定的方法只會編譯爲一個運行時代碼路徑。如您所說,唯一的選擇是檢查運行時類(直接或通過模式匹配)。 (如果沿着這條路線,Shapeless有一個類型安全的類型驅動的幫助程序)

可能有一種巧妙的方式來表達您的需求,而不會出現類型問題,但通常類型信息必須是可見的調用者或在運行時檢查。

+0

由於只有具體的實例,我不認爲這裏的運行時擦除是一個問題。或者相反,我不明白通用類型信息在這種情況下可以提供哪些幫助。必須有一個運行時檢查,但編譯器可能會確信第二個是不必要的。 –

+0

get方法是通用的,所以它不能爲不同的值賦予不同的代碼路徑。因此,調用代碼必須能夠看到「Key」/「Value」關係,或者它是運行時檢查。 – lmm

0

許多方法來解決您的類型問題。首先,確定問題的來源:

trait Map { 
    val pairs: Seq[Pair]      // (1) 
    def get[V](key: Key[V]): Option[V] =  // (2) 
    pairs.find(_.key eq key).map{_.value } // (3) 
} 
  1. pairs類型的PairSeq(內含少許不確定型key: Key[_]
  2. key類型Key[V]和預期的結果類型爲Option[V]
  3. 嘗試返回從(1)Key[_]而不是預期的Key[V]和提取的V

解決方案:你應該保證那的keypairs嵌入式類型是一樣的你回到

一個可能的解決方案:

trait Key[V] { 
    def ->(value: V) = Pair(this, value) 
} 

trait Pair { 
    type Value 
    val key: Key[Value] 
    val value: Value 
} 

trait Map1[V] { 
    val pairs: Seq[Pair {type Value = V } ] 

    def get(key: Key[V]): Option[V] = 
    pairs.find(_.key eq key).map{ _.value } 
} 

trait Map2 { 
    type Value 

    val pairs: Seq[Pair {type Value = Map2.this.Value} ] 

    def get[V >: Map2.this.Value](key: Key[V]): Option[V] = 
    pairs.find(_.key eq key).map{ _.value } 
} 
+0

對不起,我的問題含糊不清。我的意思是一張地圖可以有多個具有不同'Value'類型的鍵。我通過使用示例擴展了這個問題。 如何,我很想感謝您的答案。 –