编译时保证映射对于每个枚举情况都有一个键

Sim*_*ins 6 scala scala-3

给定以下枚举:

enum Connector:
  case CHAdeMO
  case Mennekes
  case CCS
  case Tesla
Run Code Online (Sandbox Code Playgroud)

是否有类似Map[ConnectorType, Int]但会产生编译错误的类型:

Map(
  Connector.CHAdeMO -> 1,
  Connector.Mennekes -> 2,
  Connector.CCS -> 3,
)
Run Code Online (Sandbox Code Playgroud)

也就是说,地图不包含 的键Connector.Tesla。换句话说,在编译时,类型应该类似于((Connector.CHAdeMO, Int), (Connector.Mennekes, Int), (Connector.CCS, Int), (Connector.Tesla, Int))常规 Map,但在其他方面表现得像常规 Map。

use*_*ser 5

这是使用元组( 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
Run Code Online (Sandbox Code Playgroud)

这将采用一个元组的元组(Connector, Int),并检查它是否包含另一个所有类型元组中的所有类型Connector。如果输入至少包含一次所有连接器类型,则它会查找隐式DummyImplicit,否则,它会查找隐式Nothing,但显然找不到。生成的错误消息相当冗长且无用,因此我输入了自定义错误消息。请注意,这不会检查是否存在重复的键,但可以进行简单的修改来执行此操作。

不幸的是,我发现自己必须在使用站点显式注释键值对的类型:

//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)
  )
)
Run Code Online (Sandbox Code Playgroud)