Scala,结合异构映射 - Hmap 与幺半群操作

Scala, Combining heterogeneous maps - Hmap with monoid operations

我希望创建一个异构映射,它可以包含任意数量的已定义元组:

A -> B
C -> D
E -> F
etc

现在对于每个类型 B、D、F .. 都有一个 Monoid 类型类,所以理论上我可以为我的 Heterogeneous 映射创建一个 Monoid。
有没有一种优雅的方法可以实现这一目标?

视情况而定。如果您只想合并地图,那很简单:只需 HMap.empty[YourType](hm1.underlying ++ hm2.underlying)

但是你提到幺半群表明你想合并相应位置的值。

我会说:没有附加信息是不可能的。这样的信息将是例如在编译时已知您放入 HMap 的类型的余积。

HMap 不会为您跟踪它,因此您必须自己实现它,或者通过使用一些 wrapper/builder 每次添加内容时都会更新返回的类型,例如

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 并为每个组应用此功能。

最后,您必须根据上面的代码创建一个幺半群。最有可能用于包装器,因为它已经包含您在这些隐式中真正需要的类型信息。

可能有更好的方法来实现它,但我唯一能看到的是......丑陋且不安全。