我希望创建一个可以包含任意数量的已定义元组的异构映射:
A -> B
C -> D
E -> F
etc
现在对于B,D,F的每个类型都有一个Monoid类型类,因此从理论上讲,我可以为我的异构映射创建一个Monoid。
有没有一种完美的方法来实现这一目标?
答案 0 :(得分:1)
这取决于。如果您只想合并地图,那很容易:只需HMap.empty[YourType](hm1.underlying ++ hm2.underlying)
但是您对monoid的提及表明您希望合并相应位置上的值。
我会说:没有附加信息是不可能的。这样的信息将是例如在编译时就知道了您放置在HMap中的类型的副产品。
HMap不会为您跟踪它,因此您必须自己实现它,方法是使用一些包装器/构建器,该包装器/构建器会在每次添加内容时(例如,
)更新返回的类型。class Wrapper[R[_,_], Vs <: Coproduct](hmap: HMap[R] = HMap.empty[R]) {
def +[K,V](k: K, v: V): Wrapper[R, V :+: Vs] = new Wrapper(hmap + (k,v))
}
object Wrapper {
def apply[R[_,_]]: Wrapper[R, CNil] = new Wrapper()
}
这个Coproduct
可以让您归纳地构建一些通用的类半体(尽管为简单起见,我实现了半群):
// treat it as pseudocode, sth might not work here
trait CoproductSemigroup[A] { def combine(a1: Any, a2: Any): Any }
implicit val cnilCombine = new CoproductSemigroup[CNil] { def combine(a1: Any, a2: Any): Any = ??? }
implicit val coproductCombine[H : ClassTag : Semigroup,
T <: Coproduct : CoproductSemigroup] =
new CoproductSemigroup[H :+: T] {
def combine(a1: Any, a2: Any): Any = {
if (implicitly[ClassTag[H].runtimeClass.isInstance(a1) &&
implicitly[ClassTag[H].runtimeClass.isInstance(a2))
implicitly[Semogroup[H].combine(a1.asInstanceOf[H], a2.asInstanceOf[H])]
else
implicitly[CoproductSemigroup[T]].combine(a1, a2)
}
}
已经变得很丑陋,然后您必须manually group values by the same key并将此功能应用于每个组。
最后,您必须根据上面的代码创建一个monoid。包装器最有可能,因为它已经包含了您在这些隐式中迫切需要的类型信息。
可能有更好的方法来实现它,但是我唯一看到的是……非常丑陋和不安全。