It depends. If you just want to merge maps, then it is easy: just HMap.empty[YourType](hm1.underlying ++ hm2.underlying)
But your mention about monoid suggests that you want to merge values on corresponding places.
I would say: it is not possible without additional information. Such information would be e.g. known at compile time Coproduct of types that you put in the HMap.
HMap does not trace it for you, so you would have to implement it yourself, either by using some wrapper/builder that would update the returned type each time your added something e.g.
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()
}
This Coproduct
would let you inductively build some more generic monoid (I implement semigroup though for simplicity):
// 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)
}
}
which already gets ugly, and then you sill have to manually group values by the same key and apply this function for each group.
Finally you would have to create a monoid out of the code above. Most likely for wrapper because it already contains the type information that you reaaallly need in these implicits.
Probably there is a better way of achieving it, but the only I can see is... well ugly and unsafe.