【问题标题】:Binding Existential types in Scala在 Scala 中绑定现有类型
【发布时间】:2015-06-05 05:39:23
【问题描述】:

这是我的基本 CMap,它将类(任何 T 的 Class[T])映射到任何类型的值。

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)。

【问题讨论】:

  • 不确定是否可能(以您想要的方式),因为类型擦除。这个映射总是会丢失值类型(到上限Any),并且键类型总是混合的。认为您需要一种异构的Map,来保存类型。
  • 是的,像 HMapshapeless 这样的异构地图是我正在查看的。
  • 你不能用shapeless,你要自己用吗?因为无形它非常好:)
  • 我很乐意使用shapelessHMap,如果它有效的话。在我的应用程序中,我想要一个“可变”的异构地图。我的理解是HMap 是不可变的。

标签: scala collections existential-type


【解决方案1】:

让我们尝试实现可变的HMap。 @MilesSabin 在这里对 Scala 类型系统进行了一些解释:http://www.chuusai.com/2011/07/16/fundeps-in-scala/

这个想法是静态检查构造函数(在这里你会看到,它的数量取决于你的手,所以有可能生成它或其他),以及插入方法。顺便说一句,不可变 HMap 在 shapeless 中实现的方式相同。

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))
}

所以它是我们地图的构造函数。证据将静态检查插入数据的类型。接下来,让我们包装默认的 scala 集合:

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    

按预期工作。我只实现了 insert 和 remove 函数,其他函数可以用同样的方式定义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-18
    • 2020-10-25
    • 1970-01-01
    • 1970-01-01
    • 2015-10-20
    • 1970-01-01
    相关资源
    最近更新 更多