2

I have the desire to create a heterogenous map which may contain any number of defined tuples:

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

Now for each type B, D, F .. there is a Monoid typeclass, so in theory I could create a Monoid for my Heterogeneous map.
Is there a elegant way to achieve this at all?

J Pullar
  • 1,915
  • 2
  • 18
  • 30

1 Answers1

2

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.

Mateusz Kubuszok
  • 24,995
  • 4
  • 42
  • 64