2015-03-31 51 views
2

這是我的基本CMap,它將類(任何T的類[T])映射到任何類型的值。在Scala中綁定存在類型

scala> type CMap = Map[Class[T] forSome{type T}, Any] 
defined type alias CMap 

scala> val cMap: CMap = Map(classOf[Int]->5, classOf[String]->"abc", classOf[Double]->"ddd") 
cMap: CMap = Map(int -> 5, class java.lang.String -> abc, double -> ddd) 

現在我想要一個「綁定」CMap(稱之爲CMapBind)。像CMap一樣,它將類(任何類)映射爲值(任何值)。但是,與CMAP的,CMapBind具有鍵和值之間的一種類型的結合,這意味着我希望以下行爲:

val cMapBind: CMapBind = Map(classOf[Int]->5, classOf[String]-> "aa") // should compile 
val cMapBind: CMapBind = Map(classOf[Int]->5, classOf[String]-> 0) // should fail compile 

如何實現CMapBind?

我知道以下兩個不會在語法上/邏輯上起作用。

scala> type CMapBind = Map[Class[T] forSome{type T}, T] 
<console>:8: error: not found: type T 
     type CMapBind = Map[Class[T] forSome{type T}, T] 


scala> type CMapBind = Map[Class[T], T] forSome{type T} 
scala> val cMapBind: CMapBind = Map(classOf[Int]->5, classOf[String]->"str") 
<console>:8: error: type mismatch; 
found : scala.collection.immutable.Map[Class[_ >: String with Int],Any] 
required: CMapBind 
    (which expands to) Map[Class[T],T] forSome { type T } 
     val cMapBind: CMapBind = Map(classOf[Int]->5, classOf[String]->"str") 

請注意,在這裏我使用類型構造函數Class [T]作爲示例來說明問題。在我的代碼中,我有自己的類型,例如trait Animal[S, T], class Dog extends Animal[Int, String]

編輯1: 我應該提到我使用不可變的Map作爲例子,但我真正需要的是一個可變的異構Map)。

+0

見https://github.com/akka/akka/blob/90b05abc061b2a677179d89aa64156b48a8a6afb/akka-actor/src/main/scala/akka/util/TypedMultiMap.scala – ghik 2015-03-31 19:52:50

+0

不知道這是可能的(在某種程度上要做它),由於類型擦除。這張地圖總是會丟失數值類型(上限爲'Any'),並且鍵的類型將一直是混合的。認爲你需要一種異構的'Map'來保存類型。 – DaunnC 2015-03-31 21:35:32

+0

是的,像''無形'''HMap'異質地圖是我正在看。 – Causality 2015-03-31 21:43:24

回答

0

讓我們試着實現可變HMap。關於Scala的類型系統的一些解釋是這裏由@MilesSabin:http://www.chuusai.com/2011/07/16/fundeps-in-scala/

的想法是靜態檢查構造函數(在這裏你會看到,它的參數數量取決於你的手,所以有可能產生,或不服別人)和插入方法。順便說一下,不變形的HMap也以同樣的方式實現。

import scala.collection.mutable.Map 

class HMapBuilder[R[_, _]] { // constructor arity is two 
    def apply[K1, V1](e1: (K1, V1))(implicit ev1: R[K1, V1]) = 
    new HMap[R](Map(e1)) 
    def apply[K1, V1, K2, V2](e1: (K1, V1), e2: (K2, V2)) 
          (implicit ev1: R[K1, V1], ev2: R[K2, V2]) = 
    new HMap[R](Map(e1, e2)) 
} 

所以它是我們地圖的構造函數。證據將靜態檢查插入數據的類型。接下來,讓我們換默認斯卡拉集合:

class HMap[R[_, _]](underlying : Map[Any, Any] = Map.empty) { 
    def get[K, V](k : K)(implicit ev : R[K, V]) : Option[V] = 
    underlying.get(k).asInstanceOf[Option[V]] 

    def +=[K, V](kv : (K, V))(implicit ev : R[K, V]) : HMap[R] = { 
    underlying += kv 
    this 
    } 
    def -=[K](k : K) : HMap[R] = { 
    underlying -= k 
    this 
    } 

    override def toString = underlying.toString 
} 

最後包裝HMapBuilder,使一個愉快的構造函數:

object HMap { 
    def apply[R[_, _]] = new HMapBuilder[R] 

    def empty[R[_, _]] = new HMap[R] 
    def empty[R[_, _]](underlying : Map[Any, Any]) = 
    new HMap[R](underlying) 
} 

在結果中,用法類似於shapelessHMap

class Mapping[K, V] 

implicit def mappingFromClass[A] = new Mapping[Class[A], A] 

val hm = HMap[Mapping](classOf[Int] -> 5) // ok 
hm += (classOf[String] -> "string") // ok 
hm += (classOf[Boolean] -> false) // ok 
hm += (classOf[Double] -> "sss") // compile fail  

按預期工作。我實現的只是插入和刪除函數,其他函數可以用相同的方式定義。

相關問題