score:4

Accepted answer

Here's a solution using tuples (Scastie):

opaque type CheckedMap <: Map[Connector, Int] = Map[Connector, Int]

type Contains[E, T <: Tuple] <: Boolean = T match {
  case EmptyTuple => false
  case h *: t =>
    h match {
      case (E, _) => true
      case _      => Contains[E, t]
    }
}

type ContainsAll[S <: Tuple, T <: Tuple] = S match {
  case EmptyTuple => DummyImplicit
  case h *: t =>
    Contains[h, T] match {
      case true  => ContainsAll[t, T]
      case false => Nothing
    }
}

type AllConnectors = (
    Connector.CHAdeMO.type,
    Connector.Mennekes.type,
    Connector.CCS.type,
    Connector.Tesla.type
)

def checkedMap[T <: Tuple](t: T)(using
    @annotation.implicitNotFound(
      "Not all Connector types given."
    ) c: ContainsAll[AllConnectors, T]
): CheckedMap = t.toList.asInstanceOf[List[(Connector, Int)]].toMap

This will take a tuple of tuples (Connector, Int) and check if it contains all the types in another tuple of all Connector types. If the input contains all the connector types at least once, it looks for an implicit DummyImplicit, otherwise, it looks for an implicit Nothing, which it obviously doesn't find. The resulting error message is quite lengthy and unhelpful, so I put in a custom error message. Note that this doesn't check if there are duplicate keys, but could be trivially modified to do so.

Unfortunately, I found myself having to explicitly annotate the key-value pairs' types at the use site:

//Errors because Tesla is missing
checkedMap(
  (
    Connector.CHAdeMO -> 1: (Connector.CHAdeMO.type, Int),
    Connector.Mennekes -> 2: (Connector.Mennekes.type, Int),
    Connector.CCS -> 3: (Connector.CCS.type, Int)
  )
)
//Valid
checkedMap(
  (
    Connector.CHAdeMO -> 1: (Connector.CHAdeMO.type, Int),
    Connector.Mennekes -> 2: (Connector.Mennekes.type, Int),
    Connector.CCS -> 3: (Connector.CCS.type, Int),
    Connector.Tesla -> 4: (Connector.Tesla.type, Int)
  )
)

Related Query

More Query from same tag