我能够产生一个有效的,尽管非常烦人的实现。这个指针特别有价值。
首先,几点注意事项:
对此的类型推断在很多层面上都很糟糕。测试中的所有手动类型归属以及下面的所有隐式转换都需要它才能工作。
显然 Scala 不够聪明,无法弄清楚这一点,A
并且type Id[A] = A
在寻找隐式时在功能上是相同的,因此需要临时隐式转换的组合爆炸。丑陋且不可扩展。
观察 Scala 3 中可用的不同选项:foreach
、foreachT
和foreachK
. 它们都有风格上的权衡。
如果您可以改进其中的任何一个,请告诉我。这行得通,但在 Scala 2 中要好得多。
MapK 实现:
class MapK[K[_], V[_]] protected(protected val rawMap: Map[Type[K], Type[V]]) {
def apply[A](key: K[A]): V[A] = {
rawMap(key).asInstanceOf[V[A]]
}
def updated[A](key: K[A], value: V[A]): MapK[K, V] = {
MapK.unsafeCoerce(rawMap.updated(key, value))
}
def updated[A](pair: (K[A], V[A])): MapK[K, V] = {
MapK.unsafeCoerce(rawMap.updated(pair._1, pair._2))
}
def foreach[A](f: ((K[A], V[A])) => Unit): Unit = {
rawMap.foreach(f.asInstanceOf[(([Type[K], Type[V]])) => Any])
}
def foreachT(f: Type.Tuple2[K, V] => Unit): Unit = {
foreach { (k, v) => f((k, v)) }
}
def foreachK(f: [A] => (K[A], V[A]) => Unit): Unit = {
foreach { (k, v) => f(k, v) }
}
}
object MapK {
def unsafeCoerce[K[_], V[_]](rawMap: Map[Type[K], Type[V]]): MapK[K, V] = {
new MapK[K, V](rawMap)
}
def apply[K[_], V[_]](entries: Type.Tuple2[K, V]*): MapK[K, V] = {
new MapK[K, V](Map(entries.asInstanceOf[Seq[(Type[K], Type[V])]]: _*))
}
}
您可能想要实现的 MapK 中的其他方法基本上遵循与 、 或 相同foreach
的foreachT
模式foreachK
。
现在,用法:
def test(caption: String)(code: => Unit): Unit = code
def assertEquals[A](a: A, b: A): Unit = assert(a == b)
case class Key[A](label: String, default: A)
val boolKey = Key[Boolean]("bool", false)
val intKey = Key[Int]("int", 0)
val strKey = Key[String]("str", "")
val optionMap = MapK[Key, Option](boolKey -> Some(true), intKey -> Some(1), strKey -> Option("a"), strKey -> None)
val idMap = MapK[Key, Id](boolKey -> true, intKey -> 1, strKey -> "hello")
val expectedOptionValues = List[Type.Tuple3[Key, Option, Id]](
(boolKey, Some(true), false),
(intKey, Some(1), 0),
(strKey, None, "")
)
val expectedIdValues = List[Type.Tuple3[Key, Id, Id]](
(boolKey, true, false),
(intKey, 1, 0),
(strKey, "hello", "")
)
test("optionMap - apply & updated") {
assertEquals(optionMap(intKey), Some(1))
assertEquals(optionMap(strKey), None)
assertEquals(optionMap.updated(strKey, Some("yo"))(strKey), Some("yo"))
}
test("optionMap - foreach") {
var values: List[Type.Tuple3[Key, Option, Id]] = Nil
optionMap.foreach { (k, v) =>
values = values :+ (k, v, k.default)
}
assertEquals(values, expectedOptionValues)
}
test("optionMap - foreachT") {
var values: List[Type.Tuple3[Key, Option, Id]] = Nil
optionMap.foreachT { pair => // no parameter untupling :(
values = values :+ (pair._1, pair._2, pair._1.default)
}
assertEquals(values, expectedOptionValues)
}
test("optionMap - foreachK") {
var values: List[Type.Tuple3[Key, Option, Id]] = Nil
optionMap.foreachK {
[A] => (k: Key[A], v: Option[A]) => // need explicit types :(
values = values :+ (k, v, k.default)
}
assertEquals(values, expectedOptionValues)
}
test("idMap - apply & updated") {
assertEquals(idMap(intKey), 1)
assertEquals(idMap(strKey), "hello")
assertEquals(idMap.updated(strKey, "yo")(strKey), "yo")
}
test("idMap - foreach") {
var values: List[Type.Tuple3[Key, Id, Id]] = Nil
idMap.foreach { (k, v) =>
values = values :+ (k, v, k.default)
}
assertEquals(values, expectedIdValues)
}
test("idMap - foreachT") {
var values: List[Type.Tuple3[Key, Id, Id]] = Nil
idMap.foreachT { pair =>
values = values :+ (pair._1, pair._2, pair._1.default)
}
assertEquals(values, expectedIdValues)
}
test("idMap - foreachK") {
var values: List[Type.Tuple3[Key, Id, Id]] = Nil
idMap.foreachK {
[A] => (k: Key[A], v: A) =>
values = values :+ (k, v, k.default)
}
assertEquals(values, expectedIdValues)
}
现在,使这项工作发挥作用的支持演员:
import scala.language.implicitConversions // old style, but whatever
type Id[A] = A
type Type[F[_]] <: (Any { type T })
object Type {
type Tuple2[F[_], G[_]] <: (Any { type T })
type Tuple3[F[_], G[_], H[_]] <: (Any { type T })
}
implicit def wrap[F[_], A](value: F[A]): Type[F] =
value.asInstanceOf[Type[F]]
implicit def wrapT2[F[_], G[_], A](value: (F[A], G[A])): Type.Tuple2[F, G] =
value.asInstanceOf[Type.Tuple2[F, G]]
implicit def wrapT2_P1[F[_], A](t: (F[A], A)): Type.Tuple2[F, Id] = wrapT2[F, Id, A](t)
implicit def wrapT3[F[_], G[_], H[_], A](value: (F[A], G[A], H[A])): Type.Tuple3[F, G, H] =
value.asInstanceOf[Type.Tuple3[F, G, H]]
implicit def wrapT3_P1[F[_], G[_], A](value: (F[A], A, A)): Type.Tuple3[F, Id, Id] =
value.asInstanceOf[Type.Tuple3[F, Id, Id]]
implicit def wrapT3_P1_P2[F[_], G[_], A](value: (F[A], G[A], A)): Type.Tuple3[F, G, Id] =
value.asInstanceOf[Type.Tuple3[F, G, Id]]
implicit def unwrap[F[_]](value: Type[F]): F[value.T] =
value.asInstanceOf[F[value.T]]
implicit def unwrapT2[F[_], G[_]](value: Type.Tuple2[F, G]): (F[value.T], G[value.T]) =
value.asInstanceOf[(F[value.T], G[value.T])]
implicit def unwrapT3[F[_], G[_], H[_]](value: Type.Tuple3[F, G, H]): (F[value.T], G[value.T], H[value.T]) =
value.asInstanceOf[(F[value.T], G[value.T], H[value.T])]